diff --git a/.eslintrc.yml b/.eslintrc.yml index d9f4111e..fa1ad870 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -10,7 +10,7 @@ rules: comma-spacing: error comma-style: error computed-property-spacing: error - curly: error + curly: [error, multi-line] dot-notation: error dot-location: [error, property] eol-last: error @@ -19,18 +19,18 @@ rules: indent: [error, 2, {FunctionDeclaration: {body: 1, parameters: 1}, FunctionExpression: {body: 1, parameters: 1}, CallExpression: {arguments: 1}, SwitchCase: 1, MemberExpression: 1}] key-spacing: error keyword-spacing: error - linebreak-style: [error, unix] max-statements-per-line: error new-parens: error no-caller: error no-catch-shadow: error no-console: error - no-duplicate-imports: error + no-dupe-class-members: off + no-duplicate-imports: off no-else-return: error no-eval: error no-extra-bind: error no-extra-label: error - no-extra-parens: [error, all, {nestedBinaryExpressions: false}] + no-extra-parens: off no-floating-decimal: error no-global-assign: error no-implied-eval: error @@ -46,6 +46,7 @@ rules: no-octal-escape: error no-proto: error no-prototype-builtins: error + no-redeclare: off no-restricted-properties: [error, {object: describe, property: only}, {object: it, property: only}] no-script-url: error no-self-compare: error @@ -54,10 +55,11 @@ rules: no-tabs: error no-template-curly-in-string: error no-trailing-spaces: error + no-undef: off # TypeScript will handle this no-unneeded-ternary: error no-unsafe-negation: error no-unused-expressions: error - no-unused-vars: error + no-unused-vars: off no-useless-call: error no-useless-computed-key: error no-useless-concat: error @@ -79,8 +81,8 @@ rules: prefer-spread: error quote-props: [error, as-needed] quotes: [error, single, avoid-escape] - semi: [error, always] - semi-spacing: error + # semi: [error, never] + # semi-spacing: error space-before-blocks: error space-before-function-paren: error space-in-parens: error @@ -99,12 +101,19 @@ rules: promise/catch-or-return: error promise/no-return-wrap: error promise/param-names: error - promise/no-native: error + '@typescript-eslint/consistent-type-imports': error + '@typescript-eslint/no-dupe-class-members': error + '@typescript-eslint/no-duplicate-imports': error + '@typescript-eslint/no-extra-parens': [error, all, {nestedBinaryExpressions: false}] + '@typescript-eslint/no-redeclare': error + '@typescript-eslint/no-unused-vars': error extends: - eslint:recommended - plugin:import/errors - plugin:import/warnings + - plugin:import/typescript + plugins: - import - promise @@ -123,3 +132,4 @@ parserOptions: env: node: true es6: true + browser: true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore index 297c9df8..02987e09 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ coverage/ test/_browser.spec.js oauth_info.json .eslintcache +browser/_index.js diff --git a/README.md b/README.md index c587a922..41acfc77 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# snoowrap [![Build Status](https://travis-ci.org/not-an-aardvark/snoowrap.svg?branch=master)](https://travis-ci.org/not-an-aardvark/snoowrap) [![Gitter chat](https://badges.gitter.im/not-an-aardvark/snoowrap.svg)](https://gitter.im/not-an-aardvark/snoowrap 'Join the chat at https://gitter.im/not-an-aardvark/snoowrap') +# snoowrap [![Build Status](https://travis-ci.org/not-an-aardvark/snoowrap.svg?branch=master)](https://travis-ci.org/not-an-aardvark/snoowrap) [![Gitter chat](https://badges.gitter.im/not-an-aardvark/snoowrap.svg)](https://gitter.im/not-an-aardvark/snoowrap 'Join the chat at https://gitter.im/not-an-aardvark/snoowrap') [![Discord](https://img.shields.io/discord/722505412057759917?color=%235865F2&logo=discord&logoColor=white)](https://discord.gg/vVpM7VgmGN 'Join the unofficial Discord channel at https://discord.gg/vVpM7VgmGN') A fully-featured JavaScript wrapper for the reddit API. ([Documentation](https://not-an-aardvark.github.io/snoowrap)) diff --git a/browser/index.html b/browser/index.html new file mode 100644 index 00000000..b849ab84 --- /dev/null +++ b/browser/index.html @@ -0,0 +1,26 @@ + + + + + snoowrap + + +

Use the Browser Console to Test and Debug snoowrap Manually!

+
    +
  1. To access the snoowrap class: window.snoowrap.
  2. +
  3. To access a snoowrap instance authenticated with a refresh token: window.r.
  4. +
  5. To access a snoowrap instance authenticated with username/password: window.r2.
  6. +
+

+ Use This Button to Import Files From Your Device to the Browser: + + +

+
    +
  1. This is useful if you want to upload something with snoowrap.
  2. +
  3. All imported files can be found here: window.files.
  4. +
+

Enjoy! <3

+ + + diff --git a/browser/index.js b/browser/index.js new file mode 100644 index 00000000..32dea77a --- /dev/null +++ b/browser/index.js @@ -0,0 +1,40 @@ +/* eslint-disable no-console */ +import snoowrap from '../src/snoowrap' +import BaseRequester from '../src/BaseRequester' + +window.BaseRequester = BaseRequester.default + +fetch('../oauth_info.json').then(async response => { + const oauthInfo = await response.json() + + window.r = new snoowrap({ + user_agent: oauthInfo.user_agent, + client_id: oauthInfo.client_id, + client_secret: oauthInfo.client_secret, + refresh_token: oauthInfo.refresh_token, + config: { + debug: true, + warnings: true + } + }) + + window.r2 = new snoowrap({ + user_agent: oauthInfo.user_agent, + client_id: oauthInfo.client_id, + client_secret: oauthInfo.client_secret, + username: oauthInfo.username, + password: oauthInfo.password, + config: { + debug: true, + warnings: true + } + }) +}).catch(err => console.error(err)) + +window.files = {} + +const fileinput = document.getElementById('file-input') +fileinput.onchange = () => { + const file = fileinput.files[0] + if (file) window.files[file.name] = file +} diff --git a/noop.js b/noop.js new file mode 100644 index 00000000..cc685257 --- /dev/null +++ b/noop.js @@ -0,0 +1,2 @@ +/** A fallback for builtin node modules on browser */ +export default undefined diff --git a/package-lock.json b/package-lock.json index 29bfea38..9bdffbca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,458 +1,107 @@ { "name": "snoowrap", - "version": "1.23.0", - "lockfileVersion": 1, + "version": "2.0.0", + "lockfileVersion": 2, "requires": true, - "dependencies": { - "@babel/cli": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.5.5.tgz", - "integrity": "sha512-UHI+7pHv/tk9g6WXQKYz+kmXTI77YtuY3vqC59KIqcoWEjsJJSG6rAxKaLsgj3LDyadsPrCB929gVOKM6Hui0w==", + "packages": { + "": { + "name": "snoowrap", + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "axios": "^0.21.1", + "form-data": "^4.0.0", + "lodash": "^4.17.15", + "path-browserify": "^1.0.1", + "ws": "^8.5.0" + }, + "devDependencies": { + "@babel/cli": "^7.14.8", + "@babel/core": "^7.0.0", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0", + "@babel/plugin-transform-arrow-functions": "^7.0.0", + "@babel/plugin-transform-async-to-generator": "^7.0.0", + "@babel/plugin-transform-block-scoping": "^7.0.0", + "@babel/plugin-transform-destructuring": "^7.0.0", + "@babel/plugin-transform-modules-commonjs": "^7.0.0", + "@babel/plugin-transform-parameters": "^7.0.0", + "@babel/plugin-transform-spread": "^7.0.0", + "@babel/plugin-transform-template-literals": "^7.0.0", + "@babel/plugin-transform-typescript": "^7.0.0", + "@babel/register": "^7.5.5", + "@types/lodash": "^4.14.179", + "@types/path-browserify": "^1.0.0", + "@types/request": "^2.48.2", + "@types/ws": "^8.5.3", + "@typescript-eslint/eslint-plugin": "^5.15.0", + "@typescript-eslint/parser": "^5.15.0", + "babelify": "^10.0.0", + "browserify": "^13.1.0", + "chai": "^3.5.0", + "dirty-chai": "^1.2.2", + "eslint": "^8.11.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-promise": "^6.0.0", + "five-server": "0.0.28", + "fs-extra": "^8.1.0", + "ink-docstrap": "^1.2.1", + "istanbul": "^1.0.0-alpha.2", + "jsdoc": "^3.6.7", + "mocha": "^6.0.0", + "moment": "^2.14.1", + "typedoc": "^0.22.13", + "typescript": "^4.6.2", + "uglify-js": "git://github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd", + "vite": "^2.9.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@babel/cli": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.14.8.tgz", + "integrity": "sha512-lcy6Lymft9Rpfqmrqdd4oTDdUx9ZwaAhAfywVrHG4771Pa6PPT0danJ1kDHBXYqh4HHSmIdA+nlmfxfxSDPtBg==", "dev": true, - "requires": { - "chokidar": "^2.0.4", - "commander": "^2.8.1", + "dependencies": { + "commander": "^4.0.1", "convert-source-map": "^1.1.0", "fs-readdir-recursive": "^1.1.0", "glob": "^7.0.0", - "lodash": "^4.17.13", - "mkdirp": "^0.5.1", - "output-file-sync": "^2.0.0", + "make-dir": "^2.1.0", "slash": "^2.0.0", "source-map": "^0.5.0" }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", - "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "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, - "optional": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "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, - "optional": true - }, - "output-file-sync": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz", - "integrity": "sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "is-plain-obj": "^1.1.0", - "mkdirp": "^0.5.1" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - } + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.2", + "chokidar": "^3.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/code-frame": { + "node_modules/@babel/code-frame": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", "dev": true, - "requires": { + "dependencies": { "@babel/highlight": "^7.0.0" } }, - "@babel/core": { + "node_modules/@babel/core": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.5.5", "@babel/generator": "^7.5.5", "@babel/helpers": "^7.5.5", @@ -468,328 +117,359 @@ "semver": "^5.4.1", "source-map": "^0.5.0" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/parser": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/parser": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", - "dev": true - }, - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/generator": { + "node_modules/@babel/core/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/core/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/@babel/core/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/@babel/core/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/@babel/generator": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.5.5", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0", "trim-right": "^1.0.1" - }, + } + }, + "node_modules/@babel/generator/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/helper-annotate-as-pure": { + "node_modules/@babel/generator/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/@babel/generator/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/generator/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/@babel/helper-annotate-as-pure": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.0.0" } }, - "@babel/helper-call-delegate": { + "node_modules/@babel/helper-call-delegate": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-hoist-variables": "^7.4.4", "@babel/traverse": "^7.4.4", "@babel/types": "^7.4.4" - }, + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/helper-create-class-features-plugin": { + "node_modules/@babel/helper-call-delegate/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helper-call-delegate/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/@babel/helper-create-class-features-plugin": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz", "integrity": "sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-function-name": "^7.1.0", "@babel/helper-member-expression-to-functions": "^7.5.5", "@babel/helper-optimise-call-expression": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-replace-supers": "^7.5.5", "@babel/helper-split-export-declaration": "^7.4.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/helper-function-name": { + "node_modules/@babel/helper-function-name": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-get-function-arity": "^7.0.0", "@babel/template": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@babel/helper-get-function-arity": { + "node_modules/@babel/helper-get-function-arity": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.0.0" } }, - "@babel/helper-hoist-variables": { + "node_modules/@babel/helper-hoist-variables": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.4.4" - }, + } + }, + "node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/helper-member-expression-to-functions": { + "node_modules/@babel/helper-hoist-variables/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helper-hoist-variables/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/@babel/helper-member-expression-to-functions": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz", "integrity": "sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.5.5" - }, + } + }, + "node_modules/@babel/helper-member-expression-to-functions/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/helper-module-imports": { + "node_modules/@babel/helper-member-expression-to-functions/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helper-member-expression-to-functions/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/@babel/helper-module-imports": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.0.0" } }, - "@babel/helper-module-transforms": { + "node_modules/@babel/helper-module-transforms": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz", "integrity": "sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-simple-access": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", "@babel/template": "^7.4.4", "@babel/types": "^7.5.5", "lodash": "^4.17.13" - }, + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/helper-optimise-call-expression": { + "node_modules/@babel/helper-module-transforms/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helper-module-transforms/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/@babel/helper-optimise-call-expression": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.0.0" } }, - "@babel/helper-plugin-utils": { + "node_modules/@babel/helper-plugin-utils": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", "dev": true }, - "@babel/helper-remap-async-to-generator": { + "node_modules/@babel/helper-remap-async-to-generator": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-annotate-as-pure": "^7.0.0", "@babel/helper-wrap-function": "^7.1.0", "@babel/template": "^7.1.0", @@ -797,323 +477,382 @@ "@babel/types": "^7.0.0" } }, - "@babel/helper-replace-supers": { + "node_modules/@babel/helper-replace-supers": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz", "integrity": "sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-member-expression-to-functions": "^7.5.5", "@babel/helper-optimise-call-expression": "^7.0.0", "@babel/traverse": "^7.5.5", "@babel/types": "^7.5.5" - }, + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/helper-simple-access": { + "node_modules/@babel/helper-replace-supers/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helper-replace-supers/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/@babel/helper-simple-access": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", "dev": true, - "requires": { + "dependencies": { "@babel/template": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@babel/helper-split-export-declaration": { + "node_modules/@babel/helper-split-export-declaration": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.4.4" - }, + } + }, + "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/helper-wrap-function": { + "node_modules/@babel/helper-split-export-declaration/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helper-split-export-declaration/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/@babel/helper-validator-identifier": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", + "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-function-name": "^7.1.0", "@babel/template": "^7.1.0", "@babel/traverse": "^7.1.0", "@babel/types": "^7.2.0" } }, - "@babel/helpers": { + "node_modules/@babel/helpers": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.5.tgz", "integrity": "sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==", "dev": true, - "requires": { + "dependencies": { "@babel/template": "^7.4.4", "@babel/traverse": "^7.5.5", "@babel/types": "^7.5.5" - }, + } + }, + "node_modules/@babel/helpers/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/highlight": { + "node_modules/@babel/helpers/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/helpers/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/@babel/highlight": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, - "requires": { + "dependencies": { "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/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/@babel/highlight/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": { - "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" - } - }, - "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 - }, - "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" - } - } + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/parser": { + "node_modules/@babel/highlight/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/@babel/highlight/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/@babel/parser": { "version": "7.4.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", - "dev": true + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } }, - "@babel/plugin-proposal-object-rest-spread": { + "node_modules/@babel/plugin-proposal-object-rest-spread": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz", "integrity": "sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-object-rest-spread": { + "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-typescript": { + "node_modules/@babel/plugin-syntax-typescript": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz", "integrity": "sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-arrow-functions": { + "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-async-to-generator": { + "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz", "integrity": "sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-remap-async-to-generator": "^7.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-block-scoping": { + "node_modules/@babel/plugin-transform-block-scoping": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz", "integrity": "sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "lodash": "^4.17.13" }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-destructuring": { + "node_modules/@babel/plugin-transform-block-scoping/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/plugin-transform-destructuring": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz", "integrity": "sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-modules-commonjs": { + "node_modules/@babel/plugin-transform-modules-commonjs": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz", "integrity": "sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-module-transforms": "^7.4.4", "@babel/helper-plugin-utils": "^7.0.0", "@babel/helper-simple-access": "^7.1.0", "babel-plugin-dynamic-import-node": "^2.3.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-parameters": { + "node_modules/@babel/plugin-transform-parameters": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-call-delegate": "^7.4.4", "@babel/helper-get-function-arity": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-spread": { + "node_modules/@babel/plugin-transform-spread": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-template-literals": { + "node_modules/@babel/plugin-transform-template-literals": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-annotate-as-pure": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-transform-typescript": { + "node_modules/@babel/plugin-transform-typescript": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.5.tgz", "integrity": "sha512-pehKf4m640myZu5B2ZviLaiBlxMCjSZ1qTEO459AXKX5GnPueyulJeCqZFs1nz/Ya2dDzXQ1NxZ/kKNWyD4h6w==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-create-class-features-plugin": "^7.5.5", "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-syntax-typescript": "^7.2.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/register": { + "node_modules/@babel/register": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.5.5.tgz", "integrity": "sha512-pdd5nNR+g2qDkXZlW1yRCWFlNrAn2PPdnZUB72zjX4l1Vv4fMRRLwyf+n/idFCLI1UgVGboUU8oVziwTBiyNKQ==", "dev": true, - "requires": { + "dependencies": { "core-js": "^3.0.0", "find-cache-dir": "^2.0.0", "lodash": "^4.17.13", @@ -1121,73 +860,80 @@ "pirates": "^4.0.0", "source-map-support": "^0.5.9" }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/register/node_modules/core-js": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.4.tgz", + "integrity": "sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ==", + "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true + }, + "node_modules/@babel/register/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/@babel/register/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, "dependencies": { - "core-js": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.4.tgz", - "integrity": "sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ==", - "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 - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "@babel/template": { + "node_modules/@babel/template": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.0.0", "@babel/parser": "^7.4.4", "@babel/types": "^7.4.4" - }, + } + }, + "node_modules/@babel/template/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, "dependencies": { - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "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 - } + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "@babel/traverse": { + "node_modules/@babel/template/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/@babel/template/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/@babel/traverse": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.5.5", "@babel/generator": "^7.5.5", "@babel/helper-function-name": "^7.1.0", @@ -1197,2826 +943,15198 @@ "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" }, - "dependencies": { - "@babel/parser": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", - "dev": true - }, - "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "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 - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "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": ">=6.0.0" } }, - "@babel/types": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz", - "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==", + "node_modules/@babel/traverse/node_modules/@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", "dev": true, - "requires": { + "dependencies": { "esutils": "^2.0.2", - "lodash": "^4.17.11", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "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 - } } }, - "@types/caseless": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", - "dev": true + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true + "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" + } }, - "@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", + "node_modules/@babel/traverse/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "@types/node": { - "version": "12.6.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", - "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", + "node_modules/@babel/traverse/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 }, - "@types/request": { - "version": "2.48.2", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.2.tgz", - "integrity": "sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA==", + "node_modules/@babel/traverse/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, - "requires": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.0" - }, - "dependencies": { - "form-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.0.tgz", - "integrity": "sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - } + "engines": { + "node": ">=4" } }, - "@types/tough-cookie": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", - "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz", - "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==", + "node_modules/@babel/types": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz", + "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==", "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "1.13.0", - "eslint-utils": "^1.3.1", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^2.0.1", - "tsutils": "^3.7.0" + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" } }, - "@typescript-eslint/experimental-utils": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", - "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "node_modules/@babel/types/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, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-scope": "^4.0.0" + "engines": { + "node": ">=4" } }, - "@typescript-eslint/parser": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", - "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", + "node_modules/@eslint/eslintrc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", "dev": true, - "requires": { - "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "1.13.0", - "@typescript-eslint/typescript-estree": "1.13.0", - "eslint-visitor-keys": "^1.0.0" + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "@typescript-eslint/typescript-estree": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", - "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "requires": { - "lodash.unescape": "4.0.1", - "semver": "5.5.0" - }, "dependencies": { - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", - "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", - "dev": true - }, - "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", - "dev": true - }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", - "dev": true - }, - "acorn-node": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz", - "integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==", + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "requires": { - "acorn": "^6.0.2", - "acorn-dynamic-import": "^4.0.0", - "acorn-walk": "^6.1.0", - "xtend": "^4.0.1" + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "node_modules/@eslint/eslintrc/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 }, - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "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, - "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" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "node_modules/@html-validate/stylish": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-2.0.1.tgz", + "integrity": "sha512-iRLjgQnNq66rcsTukun6KwMhPEoUV2R3atPbTSapnEvD1aETjD+pfS+1yYrmaPeJFgXHzfsSYjAuyUVq7EID/Q==", "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "dependencies": { + "kleur": "^4.0.0", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">= 12.0" } }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "node_modules/@humanwhocodes/config-array/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 }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, - "append-transform": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", - "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.2", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz", + "integrity": "sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg==", "dev": true, - "requires": { - "default-require-extensions": "^1.0.0" + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^5.1.2", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "optional": true + "engines": { + "node": ">= 8" + } }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "optional": true + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } }, - "array-filter": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", - "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", - "dev": true + "node_modules/@sidvind/better-ajv-errors": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-0.9.1.tgz", + "integrity": "sha512-V8SE37LhaixX/lb+PxdheLI8EIUi5qesdUp5VUoCN4zOJ/X/R4+Nwgc9ZvRC7/pfLXy0YfMxH2x7TVjSibA8Zg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "chalk": "^4.0.0", + "json-to-ast": "^2.0.3", + "jsonpointer": "^4.1.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">= 12.0" + }, + "peerDependencies": { + "ajv": "4.11.8 - 8" + } }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "node_modules/@sidvind/better-ajv-errors/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, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "array-map": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", - "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", - "dev": true + "node_modules/@sidvind/better-ajv-errors/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "array-reduce": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", - "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", - "dev": true + "node_modules/@sidvind/better-ajv-errors/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" + } }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "node_modules/@sidvind/better-ajv-errors/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 }, - "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" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "node_modules/@sidvind/better-ajv-errors/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, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "engines": { + "node": ">=8" } }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "node_modules/@sidvind/better-ajv-errors/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, - "requires": { - "util": "0.10.3" - }, "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "node_modules/@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true, - "optional": true + "node_modules/@types/lodash": { + "version": "4.14.179", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz", + "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==", + "dev": true }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "node_modules/@types/node": { + "version": "12.6.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", + "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", "dev": true }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "node_modules/@types/path-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/path-browserify/-/path-browserify-1.0.0.tgz", + "integrity": "sha512-XMCcyhSvxcch8b7rZAtFAaierBYdeHXVvg2iYnxOV0MCQHmPuRRmGZPFDRzPayxcGiiSL1Te9UIO+f3cuj0tfw==", "dev": true }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "node_modules/@types/request": { + "version": "2.48.2", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.2.tgz", + "integrity": "sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA==", "dev": true, - "optional": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.0.tgz", + "integrity": "sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA==", "dev": true, - "optional": true - }, - "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=" + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + "node_modules/@types/tough-cookie": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", + "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", + "dev": true }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "dependencies": { + "@types/node": "*" } }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.15.0.tgz", + "integrity": "sha512-u6Db5JfF0Esn3tiAKELvoU5TpXVSkOpZ78cEGn/wXtT2RVqs2vkt4ge6N8cRCyw7YVKhmmLDbwI2pg92mlv7cA==", "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" + "dependencies": { + "@typescript-eslint/scope-manager": "5.15.0", + "@typescript-eslint/type-utils": "5.15.0", + "@typescript-eslint/utils": "5.15.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "requires": { - "babel-runtime": "^6.22.0" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, - "requires": { - "object.assign": "^4.1.0" + "engines": { + "node": ">= 4" } }, - "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=", + "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/eslint-plugin/node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "node_modules/@typescript-eslint/parser": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.15.0.tgz", + "integrity": "sha512-NGAYP/+RDM2sVfmKiKOCgJYPstAO40vPAgACoWPO/+yoYKSgAXIFaBKsV8P0Cc7fwKgvj27SjRNX4L7f4/jCKQ==", "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "dependencies": { + "@typescript-eslint/scope-manager": "5.15.0", + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/typescript-estree": "5.15.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "babelify": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", - "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", - "dev": true - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "node_modules/@typescript-eslint/parser/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 }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.15.0.tgz", + "integrity": "sha512-EFiZcSKrHh4kWk0pZaa+YNJosvKE50EnmN4IfgjkA3bTHElPtYcd2U37QQkNTqwMCS7LXeDeZzEqnsOH8chjSg==", "dev": true, - "optional": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "dependencies": { + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/visitor-keys": "5.15.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.15.0.tgz", + "integrity": "sha512-KGeDoEQ7gHieLydujGEFLyLofipe9PIzfvA/41urz4hv+xVxPEbmMQonKSynZ0Ks2xDhJQ4VYjB3DnRiywvKDA==", + "dev": true, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, + "@typescript-eslint/utils": "5.15.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { "optional": true } } }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "node_modules/@typescript-eslint/type-utils/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 }, - "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" + "node_modules/@typescript-eslint/types": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.15.0.tgz", + "integrity": "sha512-yEiTN4MDy23vvsIksrShjNwQl2vl6kJeG9YkVJXjXZnkJElzVK8nfPsWKYxcsGWG8GhurYXP4/KGj3aZAxbeOA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "binary-extensions": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", - "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz", + "integrity": "sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA==", "dev": true, - "optional": true - }, - "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "dependencies": { + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/visitor-keys": "5.15.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "node_modules/@typescript-eslint/typescript-estree/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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 }, - "browser-pack": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", - "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "combine-source-map": "~0.8.0", - "defined": "^1.0.0", - "safe-buffer": "^5.1.1", - "through2": "^2.0.0", - "umd": "^3.0.0" + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "node_modules/@typescript-eslint/utils": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.15.0.tgz", + "integrity": "sha512-081rWu2IPKOgTOhHUk/QfxuFog8m4wxW43sXNOMSCdh578tGJ1PAaWPsj42LOa7pguh173tNlMigsbrHvh/mtA==", "dev": true, - "requires": { - "resolve": "1.1.7" - }, "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.15.0", + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/typescript-estree": "5.15.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "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==", + "node_modules/@typescript-eslint/utils/node_modules/@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, - "browserify": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-13.3.0.tgz", - "integrity": "sha1-tanJAgJD8McORnW+yCI7xifkFc4=", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz", + "integrity": "sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA==", "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "assert": "^1.4.0", - "browser-pack": "^6.0.1", - "browser-resolve": "^1.11.0", - "browserify-zlib": "~0.1.2", - "buffer": "^4.1.0", - "cached-path-relative": "^1.0.0", - "concat-stream": "~1.5.1", - "console-browserify": "^1.1.0", - "constants-browserify": "~1.0.0", - "crypto-browserify": "^3.0.0", - "defined": "^1.0.0", - "deps-sort": "^2.0.0", - "domain-browser": "~1.1.0", - "duplexer2": "~0.1.2", - "events": "~1.1.0", - "glob": "^7.1.0", - "has": "^1.0.0", - "htmlescape": "^1.1.0", - "https-browserify": "~0.0.0", - "inherits": "~2.0.1", - "insert-module-globals": "^7.0.0", - "labeled-stream-splicer": "^2.0.0", - "module-deps": "^4.0.8", - "os-browserify": "~0.1.1", - "parents": "^1.0.1", - "path-browserify": "~0.0.0", - "process": "~0.11.0", - "punycode": "^1.3.2", - "querystring-es3": "~0.2.0", - "read-only-stream": "^2.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.1.4", - "shasum": "^1.0.0", - "shell-quote": "^1.6.1", - "stream-browserify": "^2.0.0", - "stream-http": "^2.0.0", - "string_decoder": "~0.10.0", - "subarg": "^1.0.0", - "syntax-error": "^1.1.1", - "through2": "^2.0.0", - "timers-browserify": "^1.0.1", - "tty-browserify": "~0.0.0", - "url": "~0.11.0", - "util": "~0.10.1", - "vm-browserify": "~0.0.1", - "xtend": "^4.0.0" - }, "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/visitor-keys": "5.15.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/@typescript-eslint/utils/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/@typescript-eslint/utils/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, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "node_modules/@typescript-eslint/utils/node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "engines": { + "node": ">=10" } }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "node_modules/@typescript-eslint/utils/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, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "node_modules/@typescript-eslint/utils/node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "requires": { - "pako": "~0.2.0" + "engines": { + "node": ">=4.0" } }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "node_modules/@typescript-eslint/utils/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "node_modules/@typescript-eslint/utils/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 }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, - "optional": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "dependencies": { + "lru-cache": "^6.0.0" }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.15.0.tgz", + "integrity": "sha512-+vX5FKtgvyHbmIJdxMJ2jKm9z2BIlXJiuewI8dsDYMp5LzPUcuTT78Ya5iwvQg3VqSVdmxyM8Anj1Jeq7733ZQ==", + "dev": true, "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - } + "@typescript-eslint/types": "5.15.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "cached-path-relative": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", - "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", - "dev": true + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } }, - "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": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", "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==", + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, - "requires": { - "lodash": "^4.17.14" + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" } }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "node_modules/acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, - "chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "node_modules/acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", "dev": true, - "requires": { - "assertion-error": "^1.0.1", - "deep-eql": "^0.1.3", - "type-detect": "^1.0.0" + "peerDependencies": { + "acorn": "^6.0.0" } }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "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" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "node_modules/acorn-node": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz", + "integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==", + "dev": true, + "dependencies": { + "acorn": "^6.0.2", + "acorn-dynamic-import": "^4.0.0", + "acorn-walk": "^6.1.0", + "xtend": "^4.0.1" + } }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "node_modules/acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "engines": { + "node": ">=0.4.0" } }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "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, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - } + "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" } }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "requires": { - "restore-cursor": "^2.0.0" + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.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 + "node_modules/ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "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, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - } + "engines": { + "node": ">=0.10.0" } }, - "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 + "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" + } }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "optional": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, - "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==", + "node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, - "requires": { - "color-name": "1.1.3" + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "combine-source-map": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", - "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "node_modules/append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, - "requires": { - "convert-source-map": "~1.1.0", - "inline-source-map": "~0.6.0", - "lodash.memoize": "~3.0.3", - "source-map": "~0.5.3" - }, "dependencies": { - "convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", - "dev": true - } + "default-require-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "requires": { - "delayed-stream": "~1.0.0" + "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" } }, - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true, - "optional": true + "optional": true, + "engines": { + "node": ">=0.10.0" + } }, - "concat-map": { + "node_modules/array-filter": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", "dev": true }, - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "~2.0.0", - "typedarray": "~0.0.5" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes/node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, "dependencies": { - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "node_modules/array-includes/node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, - "requires": { - "date-now": "^0.1.4" + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true + "node_modules/array-includes/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true + "node_modules/array-includes/node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "node_modules/array-includes/node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, - "requires": { - "safe-buffer": "~5.1.1" + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "node_modules/array-includes/node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, - "optional": true + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", + "node_modules/array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", "dev": true }, - "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/array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "engines": { + "node": ">=8" } }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "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, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "engines": { + "node": ">=0.10.0" } }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "optional": true, + "engines": { + "node": ">=0.10.0" } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/array.prototype.flat/node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "dash-ast": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", - "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", - "dev": true + "node_modules/array.prototype.flat/node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "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" + "node_modules/array.prototype.flat/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true + "node_modules/array.prototype.flat/node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/array.prototype.flat/node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, - "requires": { - "ms": "2.0.0" + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "node_modules/array.prototype.flat/node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, - "optional": true + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "node_modules/assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", "dev": true, - "requires": { - "type-detect": "0.1.1" - }, "dependencies": { - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - } + "util": "0.10.3" } }, - "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=", + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", "dev": true }, - "default-require-extensions": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", - "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, - "requires": { - "strip-bom": "^2.0.0" - }, "dependencies": { - "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" - } - } + "inherits": "2.0.1" } }, - "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==", + "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, - "requires": { - "object-keys": "^1.0.12" + "engines": { + "node": "*" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true, "optional": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - } + "engines": { + "node": ">=0.10.0" } }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true, + "optional": true }, - "deps-sort": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", - "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "shasum": "^1.0.0", - "subarg": "^1.0.0", - "through2": "^2.0.0" + "optional": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" } }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "node_modules/axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dependencies": { + "follow-redirects": "^1.10.0" } }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, - "requires": { - "repeating": "^2.0.0" + "dependencies": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, - "detective": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", - "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "node_modules/babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, - "requires": { - "acorn": "^5.2.1", - "defined": "^1.0.0" - }, "dependencies": { - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true - } + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" } }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "dependencies": { + "babel-runtime": "^6.22.0" } }, - "dirty-chai": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/dirty-chai/-/dirty-chai-1.2.2.tgz", - "integrity": "sha1-eEleYZY19/5EIZqkyDeEm/GDFC4=", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", "dev": true, - "requires": { - "esutils": "^2.0.2" + "dependencies": { + "object.assign": "^4.1.0" } }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "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, - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "node_modules/babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, - "requires": { - "domelementtype": "1" + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" } }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "node_modules/babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" } }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, - "requires": { - "readable-stream": "^2.0.2" + "dependencies": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" } }, - "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" + "node_modules/babelify": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", + "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - } + "bin": { + "babylon": "bin/babylon.js" } }, - "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/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, - "requires": { - "once": "^1.4.0" + "optional": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "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==", + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" + "optional": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.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==", + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz", - "integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==", + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^6.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.4.1", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, + "optional": true, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "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" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "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 - }, - "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 - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", - "dev": true - }, - "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" - } - } + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" } }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "node_modules/base/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" + "optional": true, + "engines": { + "node": ">=0.10.0" } }, - "eslint-module-utils": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", - "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - } + "node_modules/base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true }, - "eslint-plugin-import": { - "version": "2.18.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", - "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true, - "requires": { - "array-includes": "^3.0.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.0", - "read-pkg-up": "^2.0.0", - "resolve": "^1.11.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } + "optional": true, + "engines": { + "node": ">=0.10.0" } }, - "eslint-plugin-promise": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", - "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "node_modules/bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" } }, - "eslint-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", - "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "node_modules/body-parser/node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, - "requires": { - "eslint-visitor-keys": "^1.0.0" + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz", - "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==", + "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, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "dev": true }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "node_modules/browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", "dev": true, - "requires": { - "estraverse": "^4.0.0" + "dependencies": { + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "JSONStream": "^1.0.3", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + }, + "bin": { + "browser-pack": "bin/cmd.js" } }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "node_modules/browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", "dev": true, - "requires": { - "estraverse": "^4.1.0" + "dependencies": { + "resolve": "1.1.7" } }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "node_modules/browser-resolve/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "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 }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true + "node_modules/browserify": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-13.3.0.tgz", + "integrity": "sha1-tanJAgJD8McORnW+yCI7xifkFc4=", + "dev": true, + "dependencies": { + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^1.11.0", + "browserify-zlib": "~0.1.2", + "buffer": "^4.1.0", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.5.1", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.0", + "domain-browser": "~1.1.0", + "duplexer2": "~0.1.2", + "events": "~1.1.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "~0.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.0.0", + "JSONStream": "^1.0.3", + "labeled-stream-splicer": "^2.0.0", + "module-deps": "^4.0.8", + "os-browserify": "~0.1.1", + "parents": "^1.0.1", + "path-browserify": "~0.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^2.0.0", + "stream-http": "^2.0.0", + "string_decoder": "~0.10.0", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "~0.0.0", + "url": "~0.11.0", + "util": "~0.10.1", + "vm-browserify": "~0.0.1", + "xtend": "^4.0.0" + }, + "bin": { + "browserify": "bin/cmd.js" + } }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "node_modules/browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, - "optional": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "dependencies": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "node_modules/browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "dev": true, + "dependencies": { + "pako": "~0.2.0" + } }, - "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/browserify/node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", "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=" + "node_modules/browserify/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "node_modules/browserify/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "node_modules/buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "deprecated": "This version of 'buffer' is out-of-date. You must update to v4.9.2 or newer", "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "node_modules/bufferutil": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", + "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" } }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "optional": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "dependencies": { - "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" - } - }, - "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" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "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-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 - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "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, - "requires": { - "locate-path": "^2.0.0" + "engines": { + "node": ">=6" } }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "requires": { - "is-buffer": "~2.0.3" + "engines": { + "node": ">=6" + } + }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, "dependencies": { - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", - "dev": true - } + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" } }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "node_modules/chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" + "dependencies": { + "assertion-error": "^1.0.1", + "deep-eql": "^0.1.3", + "type-detect": "^1.0.0" + }, + "engines": { + "node": ">= 0.4.0" } }, - "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", - "dev": true + "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" + } }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, - "optional": true + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "node_modules/chokidar/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } }, - "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" + "node_modules/chokidar/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, + "optional": true, + "engines": { + "node": ">=8" } }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/chokidar/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, "optional": true, - "requires": { - "map-cache": "^0.2.2" + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "node_modules/chokidar/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, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar/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, + "optional": true, "dependencies": { - "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", - "dev": true - } + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true + "node_modules/chokidar/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, + "optional": true, + "engines": { + "node": ">=0.12.0" + } }, - "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/chokidar/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "node_modules/chokidar/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, "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "dependencies": { + "is-number": "^7.0.0" }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "optional": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/cliui/node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/code-error-fragment": { + "version": "0.0.230", + "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", + "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "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/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "optional": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "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=", + "dev": true + }, + "node_modules/combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "dependencies": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + } + }, + "node_modules/combine-source-map/node_modules/convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true, + "optional": true + }, + "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/concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + } + }, + "node_modules/concat-stream/node_modules/process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "node_modules/console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "dependencies": { + "date-now": "^0.1.4" + } + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", + "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "dev": 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=", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "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/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, + "node_modules/date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "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/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "dependencies": { + "type-detect": "0.1.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/deep-eql/node_modules/type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true, + "dependencies": { + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-require-extensions/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/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "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/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/deps-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "dev": true, + "dependencies": { + "JSONStream": "^1.0.3", + "shasum": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + }, + "bin": { + "deps-sort": "bin/cmd.js" + } + }, + "node_modules/des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detective": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "dev": true, + "dependencies": { + "acorn": "^5.2.1", + "defined": "^1.0.0" + } + }, + "node_modules/detective/node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dirty-chai": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/dirty-chai/-/dirty-chai-1.2.2.tgz", + "integrity": "sha1-eEleYZY19/5EIZqkyDeEm/GDFC4=", + "dev": true, + "peerDependencies": { + "chai": "<1.10.0 || >1.10.0 <4" + } + }, + "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/dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "node_modules/domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/elliptic/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "dependencies": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + }, + "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==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.29.tgz", + "integrity": "sha512-SQS8cO8xFEqevYlrHt6exIhK853Me4nZ4aMW6ieysInLa0FMAL+AKs87HYNRtR2YWRcEIqoXAHh+Ytt5/66qpg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "esbuild-android-64": "0.14.29", + "esbuild-android-arm64": "0.14.29", + "esbuild-darwin-64": "0.14.29", + "esbuild-darwin-arm64": "0.14.29", + "esbuild-freebsd-64": "0.14.29", + "esbuild-freebsd-arm64": "0.14.29", + "esbuild-linux-32": "0.14.29", + "esbuild-linux-64": "0.14.29", + "esbuild-linux-arm": "0.14.29", + "esbuild-linux-arm64": "0.14.29", + "esbuild-linux-mips64le": "0.14.29", + "esbuild-linux-ppc64le": "0.14.29", + "esbuild-linux-riscv64": "0.14.29", + "esbuild-linux-s390x": "0.14.29", + "esbuild-netbsd-64": "0.14.29", + "esbuild-openbsd-64": "0.14.29", + "esbuild-sunos-64": "0.14.29", + "esbuild-windows-32": "0.14.29", + "esbuild-windows-64": "0.14.29", + "esbuild-windows-arm64": "0.14.29" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.29.tgz", + "integrity": "sha512-tJuaN33SVZyiHxRaVTo1pwW+rn3qetJX/SRuc/83rrKYtyZG0XfsQ1ao1nEudIt9w37ZSNXR236xEfm2C43sbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.29.tgz", + "integrity": "sha512-D74dCv6yYnMTlofVy1JKiLM5JdVSQd60/rQfJSDP9qvRAI0laPXIG/IXY1RG6jobmFMUfL38PbFnCqyI/6fPXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.29.tgz", + "integrity": "sha512-+CJaRvfTkzs9t+CjGa0Oa28WoXa7EeLutQhxus+fFcu0MHhsBhlmeWHac3Cc/Sf/xPi1b2ccDFfzGYJCfV0RrA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.29.tgz", + "integrity": "sha512-5Wgz/+zK+8X2ZW7vIbwoZ613Vfr4A8HmIs1XdzRmdC1kG0n5EG5fvKk/jUxhNlrYPx1gSY7XadQ3l4xAManPSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.29.tgz", + "integrity": "sha512-VTfS7Bm9QA12JK1YXF8+WyYOfvD7WMpbArtDj6bGJ5Sy5xp01c/q70Arkn596aGcGj0TvQRplaaCIrfBG1Wdtg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.29.tgz", + "integrity": "sha512-WP5L4ejwLWWvd3Fo2J5mlXvG3zQHaw5N1KxFGnUc4+2ZFZknP0ST63i0IQhpJLgEJwnQpXv2uZlU1iWZjFqEIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.29.tgz", + "integrity": "sha512-4myeOvFmQBWdI2U1dEBe2DCSpaZyjdQtmjUY11Zu2eQg4ynqLb8Y5mNjNU9UN063aVsCYYfbs8jbken/PjyidA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.29.tgz", + "integrity": "sha512-iaEuLhssReAKE7HMwxwFJFn7D/EXEs43fFy5CJeA4DGmU6JHh0qVJD2p/UP46DvUXLRKXsXw0i+kv5TdJ1w5pg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.29.tgz", + "integrity": "sha512-OXa9D9QL1hwrAnYYAHt/cXAuSCmoSqYfTW/0CEY0LgJNyTxJKtqc5mlwjAZAvgyjmha0auS/sQ0bXfGf2wAokQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.29.tgz", + "integrity": "sha512-KYf7s8wDfUy+kjKymW3twyGT14OABjGHRkm9gPJ0z4BuvqljfOOUbq9qT3JYFnZJHOgkr29atT//hcdD0Pi7Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.29.tgz", + "integrity": "sha512-05jPtWQMsZ1aMGfHOvnR5KrTvigPbU35BtuItSSWLI2sJu5VrM8Pr9Owym4wPvA4153DFcOJ1EPN/2ujcDt54g==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.29.tgz", + "integrity": "sha512-FYhBqn4Ir9xG+f6B5VIQVbRuM4S6qwy29dDNYFPoxLRnwTEKToIYIUESN1qHyUmIbfO0YB4phG2JDV2JDN9Kgw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.29.tgz", + "integrity": "sha512-eqZMqPehkb4nZcffnuOpXJQdGURGd6GXQ4ZsDHSWyIUaA+V4FpMBe+5zMPtXRD2N4BtyzVvnBko6K8IWWr36ew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.29.tgz", + "integrity": "sha512-o7EYajF1rC/4ho7kpSG3gENVx0o2SsHm7cJ5fvewWB/TEczWU7teDgusGSujxCYcMottE3zqa423VTglNTYhjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.29.tgz", + "integrity": "sha512-/esN6tb6OBSot6+JxgeOZeBk6P8V/WdR3GKBFeFpSqhgw4wx7xWUqPrdx4XNpBVO7X4Ipw9SAqgBrWHlXfddww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.29.tgz", + "integrity": "sha512-jUTdDzhEKrD0pLpjmk0UxwlfNJNg/D50vdwhrVcW/D26Vg0hVbthMfb19PJMatzclbK7cmgk1Nu0eNS+abzoHw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.29.tgz", + "integrity": "sha512-EfhQN/XO+TBHTbkxwsxwA7EfiTHFe+MNDfxcf0nj97moCppD9JHPq48MLtOaDcuvrTYOcrMdJVeqmmeQ7doTcg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.29.tgz", + "integrity": "sha512-uoyb0YAJ6uWH4PYuYjfGNjvgLlb5t6b3zIaGmpWPOjgpr1Nb3SJtQiK4YCPGhONgfg2v6DcJgSbOteuKXhwqAw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.29.tgz", + "integrity": "sha512-X9cW/Wl95QjsH8WUyr3NqbmfdU72jCp71cH3pwPvI4CgBM2IeOUDdbt6oIGljPu2bf5eGDIo8K3Y3vvXCCTd8A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.29.tgz", + "integrity": "sha512-+O/PI+68fbUZPpl3eXhqGHTGK7DjLcexNnyJqtLZXOFwoAjaXlS5UBCvVcR3o2va+AqZTj8o6URaz8D2K+yfQQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-node/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/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/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/eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", + "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "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" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/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/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "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/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/glob-parent/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/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/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/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.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.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": ">=6" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "optional": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "optional": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "optional": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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/fast-glob/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/fast-glob/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/fast-glob/node_modules/micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/fast-glob/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/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "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/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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/fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "dependencies": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/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/find-cache-dir/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/find-cache-dir/node_modules/p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/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/find-cache-dir/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/find-cache-dir/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/five-server": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/five-server/-/five-server-0.0.28.tgz", + "integrity": "sha512-N5S/ubTfmT+k++fY1ZbK3BuQ46IVcpOCHH5+8JxAtHHVrusSBXWB2GD+3QJ1JaxBcCSnSZCRmy/phsLXOxq8Rg==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.1", + "cors": "^2.8.5", + "debug": "^4.3.1", + "express": "^4.17.1", + "html-validate": "^5.2.0", + "mime-types": "~2.1.24", + "open": "^8.2.1", + "parseurl": "~1.3.3", + "selfsigned": "^1.10.8", + "ws": "^7.4.4" + }, + "bin": { + "five-server": "lib/bin.js", + "live-server": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/yandeu" + } + }, + "node_modules/five-server/node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/five-server/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/five-server/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/five-server/node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/five-server/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==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/five-server/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/five-server/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/five-server/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/five-server/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/five-server/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/five-server/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/five-server/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/five-server/node_modules/ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "deprecated": "Fixed a prototype pollution security issue in 4.1.0, please upgrade to ^4.1.1 or ^5.0.1.", + "dev": true, + "dependencies": { + "is-buffer": "~2.0.3" + }, + "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/flat-cache/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/flat/node_modules/is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/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/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "optional": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "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/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "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==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "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.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "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", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/handlebars/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/handlebars/node_modules/uglify-js": { + "version": "3.13.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz", + "integrity": "sha512-xtB8yEqIkn7zmOyS2zUNBsYCBRhDkvlNxMMY2smuJ/qA8NCHeQvKCF3i9Z4k8FJH4+PJvZRtMrPynfZ75+CSZw==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "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-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "optional": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "optional": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "optional": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "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/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/html-validate": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-5.4.0.tgz", + "integrity": "sha512-uhEVuijbJjOBsneYSCWihU4uGelc0s6swy6I/MYEBmI9z4md97rp6rwYSMhripmDcFMW2KIMPcKLDiipYFvMgw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.0", + "@html-validate/stylish": "^2.0.0", + "@sidvind/better-ajv-errors": "^0.9.0", + "acorn-walk": "^8.0.0", + "ajv": "^8.0.0", + "deepmerge": "^4.2.0", + "espree": "^7.3.0", + "glob": "^7.1.0", + "ignore": "^5.0.0", + "json-merge-patch": "^1.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.js" + }, + "engines": { + "node": ">= 12.0" + }, + "peerDependencies": { + "jest": "^24 || ^25 || ^26 || ^27", + "jest-diff": "^24 || ^25 || ^26 || ^27" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/html-validate/node_modules/@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/html-validate/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/html-validate/node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/html-validate/node_modules/acorn-walk": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", + "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "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/html-validate/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/html-validate/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/html-validate/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/html-validate/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/html-validate/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/html-validate/node_modules/ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/html-validate/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/html-validate/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/html-validate/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/html-validate/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-validate/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/htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/htmlparser2/node_modules/readable-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", + "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "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.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/ink-docstrap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/ink-docstrap/-/ink-docstrap-1.3.2.tgz", + "integrity": "sha512-STx5orGQU1gfrkoI/fMU7lX6CSP7LBGO10gXNgOZhwKhUqbtNjCkYSewJtNnLmWP1tAGN6oyEpG1HFPw5vpa5Q==", + "dev": true, + "dependencies": { + "moment": "^2.14.1", + "sanitize-html": "^1.13.0" + } + }, + "node_modules/inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "dependencies": { + "source-map": "~0.5.3" + } + }, + "node_modules/insert-module-globals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.2.0.tgz", + "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.5.2", + "combine-source-map": "^0.8.0", + "concat-stream": "^1.6.1", + "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", + "path-is-absolute": "^1.0.1", + "process": "~0.11.0", + "through2": "^2.0.0", + "undeclared-identifiers": "^1.1.2", + "xtend": "^4.0.0" + }, + "bin": { + "insert-module-globals": "bin/cmd.js" + } + }, + "node_modules/insert-module-globals/node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "optional": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/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-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "optional": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "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=", + "dev": true, + "dependencies": { + "has": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "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/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul": { + "version": "1.0.0-alpha.2", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-1.0.0-alpha.2.tgz", + "integrity": "sha1-BglrwI6Yuq10Sq5Gli2N+frGPQg=", + "dev": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "istanbul-api": "^1.0.0-alpha", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/istanbul-api": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-1.3.7.tgz", + "integrity": "sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==", + "dev": true, + "dependencies": { + "async": "^2.1.4", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.2.1", + "istanbul-lib-hook": "^1.2.2", + "istanbul-lib-instrument": "^1.10.2", + "istanbul-lib-report": "^1.1.5", + "istanbul-lib-source-maps": "^1.2.6", + "istanbul-reports": "^1.5.1", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" + } + }, + "node_modules/istanbul-api/node_modules/async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.11" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "node_modules/istanbul-lib-hook": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz", + "integrity": "sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==", + "dev": true, + "dependencies": { + "append-transform": "^0.4.0" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "dependencies": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "node_modules/istanbul-lib-report": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz", + "integrity": "sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^1.2.1", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz", + "integrity": "sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==", + "dev": true, + "dependencies": { + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.2.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/istanbul-reports": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-1.5.1.tgz", + "integrity": "sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==", + "dev": true, + "dependencies": { + "handlebars": "^4.0.3" + } + }, + "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/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/jsdoc": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.9.4", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "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": "^2.0.3", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.1" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=8.15.0" + } + }, + "node_modules/jsdoc/node_modules/@babel/parser": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", + "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "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/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.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/jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/json-merge-patch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-merge-patch/-/json-merge-patch-1.0.2.tgz", + "integrity": "sha512-M6Vp2GN9L7cfuMXiWOmHj9bEFbeC250iVtcKQbqVgEsDVYnIsrNsbU+h/Y/PkbBQCtEa4Bez+Ebv0zfbC8ObLg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/json-merge-patch/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/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==", + "dev": true + }, + "node_modules/json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "dev": true, + "dependencies": { + "jsonify": "~0.0.0" + } + }, + "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-to-ast": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", + "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "dev": true, + "dependencies": { + "code-error-fragment": "0.0.230", + "grapheme-splitter": "^1.0.4" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/json5/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/jsonpointer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", + "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.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/kleur": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", + "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/labeled-stream-splicer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz", + "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "isarray": "^2.0.4", + "stream-splicer": "^2.0.0" + } + }, + "node_modules/labeled-stream-splicer/node_modules/isarray": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", + "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==", + "dev": true + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "dependencies": { + "invert-kv": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/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/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=", + "dev": true + }, + "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=", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/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/log-symbols/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/log-symbols/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/longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "optional": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "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, + "peerDependencies": { + "markdown-it": "*" + } + }, + "node_modules/markdown-it/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/marked": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.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/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "dependencies": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "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": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "optional": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "optional": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mocha": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.0.tgz", + "integrity": "sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ==", + "dev": true, + "dependencies": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mocha/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/mocha/node_modules/cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/mocha/node_modules/cliui/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/mocha/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/mocha/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/mocha/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/mocha/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/mocha/node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/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/mocha/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/mocha/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/string-width/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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/string-width/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==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/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/mocha/node_modules/supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "dev": true, + "dependencies": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "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.0.0" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/module-deps": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "dev": true, + "dependencies": { + "browser-resolve": "^1.7.0", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.5.0", + "defined": "^1.0.0", + "detective": "^4.0.0", + "duplexer2": "^0.1.2", + "inherits": "^2.0.1", + "JSONStream": "^1.0.3", + "parents": "^1.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.3", + "stream-combiner2": "^1.1.1", + "subarg": "^1.0.0", + "through2": "^2.0.0", + "xtend": "^4.0.0" + }, + "bin": { + "module-deps": "bin/cmd.js" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", + "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "optional": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "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-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "optional": true, + "peer": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "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/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/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/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-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "optional": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "optional": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "optional": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values/node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values/node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values/node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values/node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values/node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.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=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", + "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/os-browserify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz", + "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=", + "dev": true + }, + "node_modules/os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "dependencies": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "dev": true + }, + "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/parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "dev": true, + "dependencies": { + "path-platform": "~0.11.15" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "dev": true, + "dependencies": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, + "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-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.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "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/pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "dependencies": { + "node-modules-regexp": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss/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/postcss/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/postcss/node_modules/chalk/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/postcss/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/postcss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "node_modules/prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "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/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "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/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "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/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "optional": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true, + "optional": true + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "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/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "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/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.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true, + "optional": true + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rollup": { + "version": "2.70.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz", + "integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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==", + "dev": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "optional": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sanitize-html": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.0.tgz", + "integrity": "sha512-BpxXkBoAG+uKCHjoXFmox6kCSYpnulABoGcZ/R3QyY9ndXbIM5S94eOr1IqnzTG8TnbmXaxWoDDzKC5eJv7fEQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "htmlparser2": "^3.10.0", + "lodash.clonedeep": "^4.5.0", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.mergewith": "^4.6.1", + "postcss": "^7.0.5", + "srcset": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "node_modules/sanitize-html/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/sanitize-html/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/sanitize-html/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/selfsigned": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.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/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "optional": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shasum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "dev": true, + "dependencies": { + "json-stable-stringify": "~0.0.0", + "sha.js": "~2.4.4" + } + }, + "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.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "dev": true, + "dependencies": { + "array-filter": "~0.0.0", + "array-map": "~0.0.0", + "array-reduce": "~0.0.0", + "jsonify": "~0.0.0" + } + }, + "node_modules/shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "optional": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "optional": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "optional": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true, + "optional": true + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "optional": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/srcset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", + "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.2", + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "optional": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", + "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + } + }, + "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==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "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/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "dev": true, + "dependencies": { + "minimist": "^1.1.0" + } + }, + "node_modules/subarg/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/syntax-error": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", + "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", + "dev": true, + "dependencies": { + "acorn-node": "^1.2.0" + } + }, + "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/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/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true, + "dependencies": { + "process": "~0.11.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "optional": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", + "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "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": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true, + "engines": { + "node": "*" + } + }, + "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/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/typedoc": { + "version": "0.22.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", + "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", + "dev": true, + "dependencies": { + "glob": "^7.2.0", + "lunr": "^2.3.9", + "marked": "^4.0.12", + "minimatch": "^5.0.1", + "shiki": "^0.10.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 12.10.0" + }, + "peerDependencies": { + "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x" + } + }, + "node_modules/typedoc/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typedoc/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typedoc/node_modules/marked": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", + "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typedoc/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", + "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "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": "2.7.3", + "resolved": "git+ssh://git@github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd", + "integrity": "sha512-Hp6wHD1vnDyGp4oG97X9VO4Ts3Xb0RYrOTNSWZu0a+/zxlHQGl4ENDrWRjHoOZs647ZLZiBterS1plKGAqsSHQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "node_modules/uglify-js/node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js/node_modules/decamelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz", + "integrity": "sha1-UocSL3FpHUUFsY/yJY3EAKWyOEc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js/node_modules/source-map": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.1.tgz", + "integrity": "sha1-X71Aoc3khf6rTTZGC49Jbts2Ls8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uglify-js/node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "node_modules/uglify-js/node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/uglify-js/node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, + "node_modules/umd": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", + "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", + "dev": true, + "bin": { + "umd": "bin/cli.js" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undeclared-identifiers": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", + "integrity": "sha512-pJOW4nxjlmfwKApE4zvxLScM/njmwj/DiUBv7EabwE4O8kRUy+HIwxQtZLBPll/jx1LJyBcqNfB3/cpv9EZwOw==", + "dev": true, + "dependencies": { + "acorn-node": "^1.3.0", + "dash-ast": "^1.0.0", + "get-assigned-identifiers": "^1.2.0", + "simple-concat": "^1.0.0", + "xtend": "^4.0.1" + }, + "bin": { + "undeclared-identifiers": "bin.js" + } + }, + "node_modules/underscore": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", + "dev": true + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "optional": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "optional": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "optional": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "optional": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "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==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true, + "optional": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/utf-8-validate": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", + "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.1.tgz", + "integrity": "sha512-vSlsSdOYGcYEJfkQ/NeLXgnRv5zZfpAsdztkIrs7AZHV8RCMZQkwjo4DS5BnrYTqoWqLoUe1Cah4aVO4oNNqCQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.14.27", + "postcss": "^8.4.12", + "resolve": "^1.22.0", + "rollup": "^2.59.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": ">=12.2.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "less": "*", + "sass": "*", + "stylus": "*" + }, + "peerDependenciesMeta": { + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.1", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "dependencies": { + "indexof": "0.0.1" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, + "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-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-boxed-primitive/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-boxed-primitive/node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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/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/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/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/yargs-unparser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/node_modules/cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/yargs-unparser/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/yargs-unparser/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/yargs-unparser/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/yargs-unparser/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/yargs-unparser/node_modules/p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-unparser/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/yargs-unparser/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/yargs-unparser/node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "node_modules/yargs-unparser/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/yargs-unparser/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/yargs-unparser/node_modules/yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "dependencies": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "node_modules/yargs-unparser/node_modules/yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + }, + "dependencies": { + "@babel/cli": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.14.8.tgz", + "integrity": "sha512-lcy6Lymft9Rpfqmrqdd4oTDdUx9ZwaAhAfywVrHG4771Pa6PPT0danJ1kDHBXYqh4HHSmIdA+nlmfxfxSDPtBg==", + "dev": true, + "requires": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.2", + "chokidar": "^3.4.0", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.0.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0", + "source-map": "^0.5.0" + } + }, + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", + "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", + "@babel/helpers": "^7.5.5", + "@babel/parser": "^7.5.5", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/parser": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "dev": true + }, + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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 + } + } + }, + "@babel/generator": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", + "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", + "dev": true, + "requires": { + "@babel/types": "^7.5.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-call-delegate": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz", + "integrity": "sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.4.4", + "@babel/traverse": "^7.4.4", + "@babel/types": "^7.4.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz", + "integrity": "sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-member-expression-to-functions": "^7.5.5", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.5.5", + "@babel/helper-split-export-declaration": "^7.4.4" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz", + "integrity": "sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.5.5.tgz", + "integrity": "sha512-5qZ3D1uMclSNqYcXqiHoA0meVdv+xUEex9em2fqMnrk/scphGlGgg66zjMrPJESPwrFJ6sbfFQYUSa0Mz7FabA==", + "dev": true, + "requires": { + "@babel/types": "^7.5.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz", + "integrity": "sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/types": "^7.5.5", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-wrap-function": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-replace-supers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.5.5.tgz", + "integrity": "sha512-XvRFWrNnlsow2u7jXDuH4jDDctkxbS7gXssrP4q2nUD606ukXHRvydj346wmNg+zAgpFx4MWf4+usfC93bElJg==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.5.5", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", + "dev": true, + "requires": { + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", + "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.2.0" + } + }, + "@babel/helpers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.5.tgz", + "integrity": "sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==", + "dev": true, + "requires": { + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "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 + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", + "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", + "dev": true + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz", + "integrity": "sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz", + "integrity": "sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz", + "integrity": "sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz", + "integrity": "sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + } + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz", + "integrity": "sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz", + "integrity": "sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", + "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "dev": true, + "requires": { + "@babel/helper-call-delegate": "^7.4.4", + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", + "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", + "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.5.5.tgz", + "integrity": "sha512-pehKf4m640myZu5B2ZviLaiBlxMCjSZ1qTEO459AXKX5GnPueyulJeCqZFs1nz/Ya2dDzXQ1NxZ/kKNWyD4h6w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.5.5", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-typescript": "^7.2.0" + } + }, + "@babel/register": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.5.5.tgz", + "integrity": "sha512-pdd5nNR+g2qDkXZlW1yRCWFlNrAn2PPdnZUB72zjX4l1Vv4fMRRLwyf+n/idFCLI1UgVGboUU8oVziwTBiyNKQ==", + "dev": true, + "requires": { + "core-js": "^3.0.0", + "find-cache-dir": "^2.0.0", + "lodash": "^4.17.13", + "mkdirp": "^0.5.1", + "pirates": "^4.0.0", + "source-map-support": "^0.5.9" + }, + "dependencies": { + "core-js": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.4.tgz", + "integrity": "sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ==", + "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 + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "@babel/template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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 + } + } + }, + "@babel/traverse": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", + "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.5.5", + "@babel/types": "^7.5.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/parser": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "dev": true + }, + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "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 + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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 + } + } + }, + "@babel/types": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz", + "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "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 + } + } + }, + "@eslint/eslintrc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz", + "integrity": "sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "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.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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 + }, + "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 + } + } + }, + "@html-validate/stylish": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-2.0.1.tgz", + "integrity": "sha512-iRLjgQnNq66rcsTukun6KwMhPEoUV2R3atPbTSapnEvD1aETjD+pfS+1yYrmaPeJFgXHzfsSYjAuyUVq7EID/Q==", + "dev": true, + "requires": { + "kleur": "^4.0.0", + "text-table": "^0.2.0" + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.2", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.2.tgz", + "integrity": "sha512-Fb8WxUFOBQVl+CX4MWet5o7eCc6Pj04rXIwVKZ6h1NnqTo45eOQW6aWyhG25NIODvWFwTDMwBsYxrQ3imxpetg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^5.1.2", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sidvind/better-ajv-errors": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-0.9.1.tgz", + "integrity": "sha512-V8SE37LhaixX/lb+PxdheLI8EIUi5qesdUp5VUoCN4zOJ/X/R4+Nwgc9ZvRC7/pfLXy0YfMxH2x7TVjSibA8Zg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "chalk": "^4.0.0", + "json-to-ast": "^2.0.3", + "jsonpointer": "^4.1.0", + "leven": "^3.1.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.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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" + } + } + } + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/lodash": { + "version": "4.14.179", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz", + "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==", + "dev": true + }, + "@types/node": { + "version": "12.6.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", + "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", + "dev": true + }, + "@types/path-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/path-browserify/-/path-browserify-1.0.0.tgz", + "integrity": "sha512-XMCcyhSvxcch8b7rZAtFAaierBYdeHXVvg2iYnxOV0MCQHmPuRRmGZPFDRzPayxcGiiSL1Te9UIO+f3cuj0tfw==", + "dev": true + }, + "@types/request": { + "version": "2.48.2", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.2.tgz", + "integrity": "sha512-gP+PSFXAXMrd5PcD7SqHeUjdGshAI8vKQ3+AvpQr3ht9iQea+59LOKvKITcQI+Lg+1EIkDP6AFSBUJPWG8GDyA==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.0.tgz", + "integrity": "sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/tough-cookie": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", + "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", + "dev": true + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.15.0.tgz", + "integrity": "sha512-u6Db5JfF0Esn3tiAKELvoU5TpXVSkOpZ78cEGn/wXtT2RVqs2vkt4ge6N8cRCyw7YVKhmmLDbwI2pg92mlv7cA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.15.0", + "@typescript-eslint/type-utils": "5.15.0", + "@typescript-eslint/utils": "5.15.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.15.0.tgz", + "integrity": "sha512-NGAYP/+RDM2sVfmKiKOCgJYPstAO40vPAgACoWPO/+yoYKSgAXIFaBKsV8P0Cc7fwKgvj27SjRNX4L7f4/jCKQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.15.0", + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/typescript-estree": "5.15.0", + "debug": "^4.3.2" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.15.0.tgz", + "integrity": "sha512-EFiZcSKrHh4kWk0pZaa+YNJosvKE50EnmN4IfgjkA3bTHElPtYcd2U37QQkNTqwMCS7LXeDeZzEqnsOH8chjSg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/visitor-keys": "5.15.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.15.0.tgz", + "integrity": "sha512-KGeDoEQ7gHieLydujGEFLyLofipe9PIzfvA/41urz4hv+xVxPEbmMQonKSynZ0Ks2xDhJQ4VYjB3DnRiywvKDA==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.15.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } + } + }, + "@typescript-eslint/types": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.15.0.tgz", + "integrity": "sha512-yEiTN4MDy23vvsIksrShjNwQl2vl6kJeG9YkVJXjXZnkJElzVK8nfPsWKYxcsGWG8GhurYXP4/KGj3aZAxbeOA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz", + "integrity": "sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/visitor-keys": "5.15.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.15.0.tgz", + "integrity": "sha512-081rWu2IPKOgTOhHUk/QfxuFog8m4wxW43sXNOMSCdh578tGJ1PAaWPsj42LOa7pguh173tNlMigsbrHvh/mtA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.15.0", + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/typescript-estree": "5.15.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.15.0.tgz", + "integrity": "sha512-Hb0e3dGc35b75xLzixM3cSbG1sSbrTBQDfIScqdyvrfJZVEi4XWAT+UL/HMxEdrJNB8Yk28SKxPLtAhfCbBInA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.15.0", + "@typescript-eslint/visitor-keys": "5.15.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.15.0.tgz", + "integrity": "sha512-+vX5FKtgvyHbmIJdxMJ2jKm9z2BIlXJiuewI8dsDYMp5LzPUcuTT78Ya5iwvQg3VqSVdmxyM8Anj1Jeq7733ZQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.15.0", + "eslint-visitor-keys": "^3.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + } + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", + "dev": true, + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-node": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.6.2.tgz", + "integrity": "sha512-rIhNEZuNI8ibQcL7ANm/mGyPukIaZsRNX9psFNQURyJW0nu6k8wjSDld20z6v2mDBWqX13pIEnk9gGZJHIlEXg==", + "dev": true, + "requires": { + "acorn": "^6.0.2", + "acorn-dynamic-import": "^4.0.0", + "acorn-walk": "^6.1.0", + "xtend": "^4.0.1" + } + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true + }, + "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" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "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 + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "append-transform": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", + "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", + "dev": true, + "requires": { + "default-require-extensions": "^1.0.0" + } + }, + "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" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "optional": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "optional": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "optional": true + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "dependencies": { + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", + "dev": true + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "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 + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "optional": true + }, + "array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "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 + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "optional": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "optional": true + }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babelify": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz", + "integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==", + "dev": true, + "requires": {} + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "optional": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.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": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-pack": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", + "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", + "dev": true, + "requires": { + "combine-source-map": "~0.8.0", + "defined": "^1.0.0", + "JSONStream": "^1.0.3", + "safe-buffer": "^5.1.1", + "through2": "^2.0.0", + "umd": "^3.0.0" + } + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "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 + }, + "browserify": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-13.3.0.tgz", + "integrity": "sha1-tanJAgJD8McORnW+yCI7xifkFc4=", + "dev": true, + "requires": { + "assert": "^1.4.0", + "browser-pack": "^6.0.1", + "browser-resolve": "^1.11.0", + "browserify-zlib": "~0.1.2", + "buffer": "^4.1.0", + "cached-path-relative": "^1.0.0", + "concat-stream": "~1.5.1", + "console-browserify": "^1.1.0", + "constants-browserify": "~1.0.0", + "crypto-browserify": "^3.0.0", + "defined": "^1.0.0", + "deps-sort": "^2.0.0", + "domain-browser": "~1.1.0", + "duplexer2": "~0.1.2", + "events": "~1.1.0", + "glob": "^7.1.0", + "has": "^1.0.0", + "htmlescape": "^1.1.0", + "https-browserify": "~0.0.0", + "inherits": "~2.0.1", + "insert-module-globals": "^7.0.0", + "JSONStream": "^1.0.3", + "labeled-stream-splicer": "^2.0.0", + "module-deps": "^4.0.8", + "os-browserify": "~0.1.1", + "parents": "^1.0.1", + "path-browserify": "~0.0.0", + "process": "~0.11.0", + "punycode": "^1.3.2", + "querystring-es3": "~0.2.0", + "read-only-stream": "^2.0.0", + "readable-stream": "^2.0.2", + "resolve": "^1.1.4", + "shasum": "^1.0.0", + "shell-quote": "^1.6.1", + "stream-browserify": "^2.0.0", + "stream-http": "^2.0.0", + "string_decoder": "~0.10.0", + "subarg": "^1.0.0", + "syntax-error": "^1.1.1", + "through2": "^2.0.0", + "timers-browserify": "^1.0.1", + "tty-browserify": "~0.0.0", + "url": "~0.11.0", + "util": "~0.10.1", + "vm-browserify": "~0.0.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "dev": true, + "requires": { + "pako": "~0.2.0" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "bufferutil": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.6.tgz", + "integrity": "sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw==", + "optional": true, + "peer": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "optional": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cached-path-relative": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", + "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "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==", + "dev": true + }, + "catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true, + "requires": { + "assertion-error": "^1.0.1", + "deep-eql": "^0.1.3", + "type-detect": "^1.0.0" + } + }, + "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" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "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, + "optional": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "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, + "optional": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "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, + "optional": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "optional": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + } + } + }, + "code-error-fragment": { + "version": "0.0.230", + "resolved": "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz", + "integrity": "sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==", + "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 + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "optional": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "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=", + "dev": true + }, + "combine-source-map": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", + "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", + "dev": true, + "requires": { + "convert-source-map": "~1.1.0", + "inline-source-map": "~0.6.0", + "lodash.memoize": "~3.0.3", + "source-map": "~0.5.3" + }, + "dependencies": { + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", + "dev": true + } + } + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true, + "optional": 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 + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~2.0.0", + "typedarray": "~0.0.5" + }, + "dependencies": { + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "optional": true + }, + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", + "dev": true + }, + "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=", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "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" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "dash-ast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", + "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "optional": true + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "default-require-extensions": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", + "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", + "dev": true, + "requires": { + "strip-bom": "^2.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, "optional": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "deps-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "dev": true, + "requires": { + "JSONStream": "^1.0.3", + "shasum": "^1.0.0", + "subarg": "^1.0.0", + "through2": "^2.0.0" + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detective": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "dev": true, + "requires": { + "acorn": "^5.2.1", + "defined": "^1.0.0" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + } + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dirty-chai": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/dirty-chai/-/dirty-chai-1.2.2.tgz", + "integrity": "sha1-eEleYZY19/5EIZqkyDeEm/GDFC4=", + "dev": true, + "requires": {} + }, + "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" + } + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "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==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "esbuild": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.29.tgz", + "integrity": "sha512-SQS8cO8xFEqevYlrHt6exIhK853Me4nZ4aMW6ieysInLa0FMAL+AKs87HYNRtR2YWRcEIqoXAHh+Ytt5/66qpg==", + "dev": true, + "requires": { + "esbuild-android-64": "0.14.29", + "esbuild-android-arm64": "0.14.29", + "esbuild-darwin-64": "0.14.29", + "esbuild-darwin-arm64": "0.14.29", + "esbuild-freebsd-64": "0.14.29", + "esbuild-freebsd-arm64": "0.14.29", + "esbuild-linux-32": "0.14.29", + "esbuild-linux-64": "0.14.29", + "esbuild-linux-arm": "0.14.29", + "esbuild-linux-arm64": "0.14.29", + "esbuild-linux-mips64le": "0.14.29", + "esbuild-linux-ppc64le": "0.14.29", + "esbuild-linux-riscv64": "0.14.29", + "esbuild-linux-s390x": "0.14.29", + "esbuild-netbsd-64": "0.14.29", + "esbuild-openbsd-64": "0.14.29", + "esbuild-sunos-64": "0.14.29", + "esbuild-windows-32": "0.14.29", + "esbuild-windows-64": "0.14.29", + "esbuild-windows-arm64": "0.14.29" + } + }, + "esbuild-android-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.29.tgz", + "integrity": "sha512-tJuaN33SVZyiHxRaVTo1pwW+rn3qetJX/SRuc/83rrKYtyZG0XfsQ1ao1nEudIt9w37ZSNXR236xEfm2C43sbw==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.29.tgz", + "integrity": "sha512-D74dCv6yYnMTlofVy1JKiLM5JdVSQd60/rQfJSDP9qvRAI0laPXIG/IXY1RG6jobmFMUfL38PbFnCqyI/6fPXg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.29.tgz", + "integrity": "sha512-+CJaRvfTkzs9t+CjGa0Oa28WoXa7EeLutQhxus+fFcu0MHhsBhlmeWHac3Cc/Sf/xPi1b2ccDFfzGYJCfV0RrA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.29.tgz", + "integrity": "sha512-5Wgz/+zK+8X2ZW7vIbwoZ613Vfr4A8HmIs1XdzRmdC1kG0n5EG5fvKk/jUxhNlrYPx1gSY7XadQ3l4xAManPSw==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.29.tgz", + "integrity": "sha512-VTfS7Bm9QA12JK1YXF8+WyYOfvD7WMpbArtDj6bGJ5Sy5xp01c/q70Arkn596aGcGj0TvQRplaaCIrfBG1Wdtg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.29.tgz", + "integrity": "sha512-WP5L4ejwLWWvd3Fo2J5mlXvG3zQHaw5N1KxFGnUc4+2ZFZknP0ST63i0IQhpJLgEJwnQpXv2uZlU1iWZjFqEIg==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.29.tgz", + "integrity": "sha512-4myeOvFmQBWdI2U1dEBe2DCSpaZyjdQtmjUY11Zu2eQg4ynqLb8Y5mNjNU9UN063aVsCYYfbs8jbken/PjyidA==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.29.tgz", + "integrity": "sha512-iaEuLhssReAKE7HMwxwFJFn7D/EXEs43fFy5CJeA4DGmU6JHh0qVJD2p/UP46DvUXLRKXsXw0i+kv5TdJ1w5pg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.29.tgz", + "integrity": "sha512-OXa9D9QL1hwrAnYYAHt/cXAuSCmoSqYfTW/0CEY0LgJNyTxJKtqc5mlwjAZAvgyjmha0auS/sQ0bXfGf2wAokQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.29.tgz", + "integrity": "sha512-KYf7s8wDfUy+kjKymW3twyGT14OABjGHRkm9gPJ0z4BuvqljfOOUbq9qT3JYFnZJHOgkr29atT//hcdD0Pi7Mw==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.29.tgz", + "integrity": "sha512-05jPtWQMsZ1aMGfHOvnR5KrTvigPbU35BtuItSSWLI2sJu5VrM8Pr9Owym4wPvA4153DFcOJ1EPN/2ujcDt54g==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.29.tgz", + "integrity": "sha512-FYhBqn4Ir9xG+f6B5VIQVbRuM4S6qwy29dDNYFPoxLRnwTEKToIYIUESN1qHyUmIbfO0YB4phG2JDV2JDN9Kgw==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.29.tgz", + "integrity": "sha512-eqZMqPehkb4nZcffnuOpXJQdGURGd6GXQ4ZsDHSWyIUaA+V4FpMBe+5zMPtXRD2N4BtyzVvnBko6K8IWWr36ew==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.29.tgz", + "integrity": "sha512-o7EYajF1rC/4ho7kpSG3gENVx0o2SsHm7cJ5fvewWB/TEczWU7teDgusGSujxCYcMottE3zqa423VTglNTYhjg==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.29.tgz", + "integrity": "sha512-/esN6tb6OBSot6+JxgeOZeBk6P8V/WdR3GKBFeFpSqhgw4wx7xWUqPrdx4XNpBVO7X4Ipw9SAqgBrWHlXfddww==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.29.tgz", + "integrity": "sha512-jUTdDzhEKrD0pLpjmk0UxwlfNJNg/D50vdwhrVcW/D26Vg0hVbthMfb19PJMatzclbK7cmgk1Nu0eNS+abzoHw==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.29.tgz", + "integrity": "sha512-EfhQN/XO+TBHTbkxwsxwA7EfiTHFe+MNDfxcf0nj97moCppD9JHPq48MLtOaDcuvrTYOcrMdJVeqmmeQ7doTcg==", + "dev": true, + "optional": true + }, + "esbuild-windows-32": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.29.tgz", + "integrity": "sha512-uoyb0YAJ6uWH4PYuYjfGNjvgLlb5t6b3zIaGmpWPOjgpr1Nb3SJtQiK4YCPGhONgfg2v6DcJgSbOteuKXhwqAw==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.29.tgz", + "integrity": "sha512-X9cW/Wl95QjsH8WUyr3NqbmfdU72jCp71cH3pwPvI4CgBM2IeOUDdbt6oIGljPu2bf5eGDIo8K3Y3vvXCCTd8A==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.29", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.29.tgz", + "integrity": "sha512-+O/PI+68fbUZPpl3eXhqGHTGK7DjLcexNnyJqtLZXOFwoAjaXlS5UBCvVcR3o2va+AqZTj8o6URaz8D2K+yfQQ==", + "dev": true, + "optional": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.11.0.tgz", + "integrity": "sha512-/KRpd9mIRg2raGxHRGwW9ZywYNAClZrHjdueHcrVDuO3a6bj83eoTirCCk0M0yPwOjWYKHwRVRid+xK4F/GHgA==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.2.1", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "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" } }, - "chownr": { - "version": "1.1.1", - "bundled": true, + "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 + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "optional": true + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, + "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, - "optional": true + "requires": { + "color-name": "~1.1.4" + } }, - "concat-map": { - "version": "0.0.1", - "bundled": true, + "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, - "optional": true + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "optional": true + "requires": { + "ms": "2.1.2" + } }, - "core-util-is": { - "version": "1.0.2", - "bundled": 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 + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "optional": true + "requires": { + "is-glob": "^4.0.3" + }, + "dependencies": { + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } + } }, - "debug": { - "version": "4.1.1", - "bundled": true, + "globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", "dev": true, - "optional": true, "requires": { - "ms": "^2.1.1" + "type-fest": "^0.20.2" } }, - "deep-extend": { - "version": "0.6.0", - "bundled": 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 + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "optional": true + "requires": { + "argparse": "^2.0.1" + } }, - "delegates": { - "version": "1.0.0", - "bundled": true, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "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, - "optional": true + "requires": { + "shebang-regex": "^3.0.0" + } }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, + "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.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "optional": true + "requires": { + "ansi-regex": "^5.0.1" + } }, - "fs-minipass": { - "version": "1.2.5", - "bundled": 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": "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, - "optional": true, "requires": { - "minipass": "^2.2.1" + "has-flag": "^4.0.0" } }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "optional": true + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } }, - "gauge": { - "version": "2.7.4", - "bundled": true, + "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 + } + } + }, + "eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "optional": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "ms": "^2.1.1" } }, - "glob": { - "version": "7.1.3", - "bundled": true, + "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 + } + } + }, + "eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } + } + }, + "eslint-plugin-promise": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz", + "integrity": "sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + } + } + }, + "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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.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" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "optional": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "optional": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "is-descriptor": "^0.1.0" } }, - "has-unicode": { + "extend-shallow": { "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "is-extendable": "^0.1.0" } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, + } + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "optional": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "is-plain-object": "^2.0.4" } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "optional": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { "version": "1.0.0", - "bundled": true, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "optional": true, "requires": { - "number-is-nan": "^1.0.0" + "is-descriptor": "^1.0.0" } }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "optional": true, "requires": { - "brace-expansion": "^1.1.7" + "is-extendable": "^0.1.0" } }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "optional": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "kind-of": "^6.0.0" } }, - "minizlib": { - "version": "1.2.1", - "bundled": true, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "kind-of": "^6.0.0" } }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "optional": true, "requires": { - "minimist": "0.0.8" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } }, - "ms": { - "version": "2.1.1", - "bundled": true, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": 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 + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "fill-range": "^7.0.1" } }, - "nopt": { - "version": "4.0.1", - "bundled": true, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "to-regex-range": "^5.0.1" } }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, - "npmlog": { - "version": "4.1.2", - "bundled": true, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, - "optional": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "optional": true, "requires": { - "wrappy": "1" + "is-number": "^7.0.0" } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "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 + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "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" + } + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "is-extendable": "^0.1.0" } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "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" } }, - "readable-stream": { - "version": "2.3.6", - "bundled": 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, - "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, - "rimraf": { - "version": "2.6.3", - "bundled": true, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, - "optional": true, "requires": { - "glob": "^7.1.3" + "p-try": "^2.0.0" } }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, + "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, - "optional": true + "requires": { + "p-limit": "^2.0.0" + } }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true + "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 }, - "sax": { - "version": "1.2.4", - "bundled": true, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "five-server": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/five-server/-/five-server-0.0.28.tgz", + "integrity": "sha512-N5S/ubTfmT+k++fY1ZbK3BuQ46IVcpOCHH5+8JxAtHHVrusSBXWB2GD+3QJ1JaxBcCSnSZCRmy/phsLXOxq8Rg==", + "dev": true, + "requires": { + "chokidar": "^3.5.1", + "cors": "^2.8.5", + "debug": "^4.3.1", + "express": "^4.17.1", + "html-validate": "^5.2.0", + "mime-types": "~2.1.24", + "open": "^8.2.1", + "parseurl": "~1.3.3", + "selfsigned": "^1.10.8", + "ws": "^7.4.4" + }, + "dependencies": { + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "optional": true + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true + "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 }, - "signal-exit": { + "braces": { "version": "3.0.2", - "bundled": true, + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "optional": true + "requires": { + "fill-range": "^7.0.1" + } }, - "string-width": { - "version": "1.0.2", - "bundled": true, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, - "optional": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" } }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, - "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "ms": "2.1.2" } }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "optional": true, "requires": { - "ansi-regex": "^2.0.0" + "to-regex-range": "^5.0.1" } }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "optional": true + "requires": { + "is-glob": "^4.0.1" + } }, - "tar": { - "version": "4.4.8", - "bundled": true, + "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, - "optional": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "binary-extensions": "^2.0.0" } }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true + "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 }, - "wide-align": { - "version": "1.1.3", - "bundled": true, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "optional": true, "requires": { - "string-width": "^1.0.2 || 2" + "picomatch": "^2.2.1" } }, - "wrappy": { - "version": "1.0.2", - "bundled": true, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "optional": true + "requires": { + "is-number": "^7.0.0" + } }, - "yallist": { - "version": "3.0.3", - "bundled": true, + "ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", "dev": true, - "optional": true + "requires": {} + } + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "follow-redirects": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", + "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "optional": true + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "dependencies": { + "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" + } } } }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "optional": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "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 + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -4041,6 +16159,25 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + } + } + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -4050,6 +16187,16 @@ "pump": "^3.0.0" } }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -4057,14 +16204,6 @@ "dev": true, "optional": 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" - } - }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -4080,24 +16219,58 @@ } }, "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, "growl": { @@ -4140,43 +16313,6 @@ } } }, - "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.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "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==" - } - } - }, - "harmony-reflect": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz", - "integrity": "sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==" - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4195,6 +16331,12 @@ "ansi-regex": "^2.0.0" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -4207,6 +16349,23 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + } + } + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -4217,15 +16376,6 @@ "get-value": "^2.0.6", "has-values": "^1.0.0", "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - } } }, "has-values": { @@ -4239,28 +16389,6 @@ "kind-of": "^4.0.0" }, "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -4310,11 +16438,165 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "html-validate": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-5.4.0.tgz", + "integrity": "sha512-uhEVuijbJjOBsneYSCWihU4uGelc0s6swy6I/MYEBmI9z4md97rp6rwYSMhripmDcFMW2KIMPcKLDiipYFvMgw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.0", + "@html-validate/stylish": "^2.0.0", + "@sidvind/better-ajv-errors": "^0.9.0", + "acorn-walk": "^8.0.0", + "ajv": "^8.0.0", + "deepmerge": "^4.2.0", + "espree": "^7.3.0", + "glob": "^7.1.0", + "ignore": "^5.0.0", + "json-merge-patch": "^1.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", + "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==", + "dev": true + }, + "ajv": { + "version": "8.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", + "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "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-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" + } + }, + "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" + } + }, + "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 + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "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 + }, + "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 + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.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" + } + } + } }, "htmlescape": { "version": "1.1.1", @@ -4349,14 +16631,25 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } } }, "https-browserify": { @@ -4381,15 +16674,15 @@ "dev": true }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "import-fresh": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", - "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -4429,120 +16722,18 @@ "resolved": "https://registry.npmjs.org/ink-docstrap/-/ink-docstrap-1.3.2.tgz", "integrity": "sha512-STx5orGQU1gfrkoI/fMU7lX6CSP7LBGO10gXNgOZhwKhUqbtNjCkYSewJtNnLmWP1tAGN6oyEpG1HFPw5vpa5Q==", "dev": true, - "requires": { - "moment": "^2.14.1", - "sanitize-html": "^1.13.0" - } - }, - "inline-source-map": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", - "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", - "dev": true, - "requires": { - "source-map": "~0.5.3" - } - }, - "inquirer": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz", - "integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "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" - }, - "dependencies": { - "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" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } - } - }, - "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" - } - } + "requires": { + "moment": "^2.14.1", + "sanitize-html": "^1.13.0" + } + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "dev": true, + "requires": { + "source-map": "~0.5.3" } }, "insert-module-globals": { @@ -4551,11 +16742,11 @@ "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "acorn-node": "^1.5.2", "combine-source-map": "^0.8.0", "concat-stream": "^1.6.1", "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", "path-is-absolute": "^1.0.1", "process": "~0.11.0", "through2": "^2.0.0", @@ -4577,6 +16768,17 @@ } } }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -4592,6 +16794,12 @@ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -4602,11 +16810,14 @@ "kind-of": "^3.0.2" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } }, "is-binary-path": { "version": "1.0.1", @@ -4618,6 +16829,16 @@ "binary-extensions": "^1.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -4630,6 +16851,15 @@ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, + "is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -4667,6 +16897,12 @@ } } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -4707,12 +16943,31 @@ "is-extglob": "^2.1.1" } }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -4721,23 +16976,8 @@ "optional": true, "requires": { "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - } } }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -4747,12 +16987,27 @@ "has": "^1.0.1" } }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "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-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -4762,17 +17017,21 @@ "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-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -4792,10 +17051,12 @@ "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=" + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "optional": true }, "istanbul": { "version": "1.0.0-alpha.2", @@ -4958,51 +17219,58 @@ } }, "js2xmlparser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.0.tgz", - "integrity": "sha512-WuNgdZOXVmBk5kUPMcTcVUpbGRzLfNkv7+7APq7WiDihpXVKrgxo6wwRpRl9OQeEBgKCVk9mR7RbzrnNWC8oBw==", + "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.0" + "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.3", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.3.tgz", - "integrity": "sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.7.tgz", + "integrity": "sha512-sxKt7h0vzCd+3Y81Ey2qinupL6DpRSZJclS04ugHDNmRUXGzqicMJ6iwayhSA0S0DwwX30c5ozyUthr1QKF6uw==", "dev": true, "requires": { - "@babel/parser": "^7.4.4", - "bluebird": "^3.5.4", - "catharsis": "^0.8.11", + "@babel/parser": "^7.9.4", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.0", + "js2xmlparser": "^4.0.1", "klaw": "^3.0.0", - "markdown-it": "^8.4.2", - "markdown-it-anchor": "^5.0.2", - "marked": "^0.7.0", - "mkdirp": "^0.5.1", + "markdown-it": "^10.0.0", + "markdown-it-anchor": "^5.2.7", + "marked": "^2.0.3", + "mkdirp": "^1.0.4", "requizzle": "^0.2.3", - "strip-json-comments": "^3.0.1", + "strip-json-comments": "^3.1.0", "taffydb": "2.6.2", - "underscore": "~1.9.1" + "underscore": "~1.13.1" }, "dependencies": { + "@babel/parser": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.3.tgz", + "integrity": "sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==", + "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 }, + "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.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true } } @@ -5013,15 +17281,28 @@ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "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-merge-patch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-merge-patch/-/json-merge-patch-1.0.2.tgz", + "integrity": "sha512-M6Vp2GN9L7cfuMXiWOmHj9bEFbeC250iVtcKQbqVgEsDVYnIsrNsbU+h/Y/PkbBQCtEa4Bez+Ebv0zfbC8ObLg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + }, + "dependencies": { + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + } + } }, "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==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify": { "version": "0.0.1", @@ -5038,10 +17319,38 @@ "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-ast": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json-to-ast/-/json-to-ast-2.1.0.tgz", + "integrity": "sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==", + "dev": true, + "requires": { + "code-error-fragment": "0.0.230", + "grapheme-splitter": "^1.0.4" + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true }, "jsonfile": { "version": "4.0.0", @@ -5064,15 +17373,20 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "jsonpointer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.1.0.tgz", + "integrity": "sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" } }, "kind-of": { @@ -5093,6 +17407,12 @@ "graceful-fs": "^4.1.9" } }, + "kleur": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz", + "integrity": "sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==", + "dev": true + }, "labeled-stream-splicer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz", @@ -5127,14 +17447,20 @@ "invert-kv": "^2.0.0" } }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "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.1.2", - "type-check": "~0.3.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "linkify-it": { @@ -5146,18 +17472,6 @@ "uc.micro": "^1.0.1" } }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -5203,18 +17517,18 @@ "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", "dev": true }, + "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==", + "dev": true + }, "lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, - "lodash.unescape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", - "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", - "dev": true - }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -5270,6 +17584,21 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -5315,28 +17644,37 @@ } }, "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "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": "~1.1.1", + "entities": "~2.0.0", "linkify-it": "^2.0.0", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" + }, + "dependencies": { + "entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true + } } }, "markdown-it-anchor": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.4.tgz", - "integrity": "sha512-n8zCGjxA3T+Mx1pG8HEgbJbkB8JFUuRkeTZQuIM8iPY6oQ8sWOPRZJDFC9a/pNg2QkHEjjGkhBEl/RSyzaDZ3A==", - "dev": true + "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, + "requires": {} }, "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.1.3.tgz", + "integrity": "sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==", "dev": true }, "md5.js": { @@ -5356,6 +17694,12 @@ "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", "dev": true }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, "mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -5367,6 +17711,55 @@ "p-is-promise": "^2.0.0" } }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "optional": true + } + } + }, "miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -5377,17 +17770,23 @@ "brorand": "^1.0.1" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -5447,12 +17846,20 @@ } }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } } }, "mocha": { @@ -5549,6 +17956,15 @@ "path-exists": "^3.0.0" } }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -5643,6 +18059,16 @@ "y18n": "^4.0.0", "yargs-parser": "^13.0.0" } + }, + "yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, @@ -5652,7 +18078,6 @@ "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", "dev": true, "requires": { - "JSONStream": "^1.0.3", "browser-resolve": "^1.7.0", "cached-path-relative": "^1.0.0", "concat-stream": "~1.5.0", @@ -5660,6 +18085,7 @@ "detective": "^4.0.0", "duplexer2": "^0.1.2", "inherits": "^2.0.1", + "JSONStream": "^1.0.3", "parents": "^1.0.0", "readable-stream": "^2.0.2", "resolve": "^1.1.3", @@ -5681,19 +18107,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "nanoid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.2.tgz", + "integrity": "sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==", "dev": true }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -5714,24 +18133,10 @@ "to-regex": "^3.0.1" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "optional": true } @@ -5743,6 +18148,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", @@ -5773,6 +18184,19 @@ } } }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", + "optional": true, + "peer": true + }, "node-modules-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", @@ -5788,17 +18212,11 @@ "abbrev": "1" } }, - "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 }, "npm-run-path": { "version": "2.0.2", @@ -5815,10 +18233,11 @@ "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-copy": { "version": "0.1.0", @@ -5844,6 +18263,12 @@ } } }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -5858,15 +18283,6 @@ "optional": true, "requires": { "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - } } }, "object.assign": { @@ -5899,27 +18315,101 @@ "optional": true, "requires": { "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" }, "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dev": true, - "optional": true + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } } } }, - "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" + "ee-first": "1.1.1" } }, "once": { @@ -5931,35 +18421,40 @@ "wrappy": "1" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "open": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz", + "integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "dependencies": { - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } } } }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "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.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "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" } }, "os-browserify": { @@ -5979,12 +18474,6 @@ "mem": "^4.0.0" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -6065,14 +18554,11 @@ "safe-buffer": "^5.1.1" } }, - "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" - } + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true }, "pascalcase": { "version": "0.1.1", @@ -6082,17 +18568,9 @@ "optional": true }, "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true, - "optional": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" }, "path-exists": { "version": "3.0.0", @@ -6113,9 +18591,9 @@ "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==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-platform": { @@ -6124,584 +18602,306 @@ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", "dev": true }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "optional": true - }, - "postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.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" - }, - "dependencies": { - "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" - } - } - } - }, - "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 - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promise-chains": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/promise-chains/-/promise-chains-0.3.12.tgz", - "integrity": "sha1-aOY0hMm5YvHW4qtnIyTRT1kJ7iE=", - "requires": { - "harmony-reflect": "^1.4.3" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "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" - } + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } + "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 }, - "read-only-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", - "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "dev": true, "requires": { - "readable-stream": "^2.0.2" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", "dev": true, "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" + "node-modules-regexp": "^1.0.0" } }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "optional": true }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", "dev": true, - "optional": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" }, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "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, - "optional": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "color-convert": "^1.9.0" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "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, - "optional": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "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, - "optional": true, "requires": { - "is-extendable": "^0.1.0" + "has-flag": "^3.0.0" } } } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } + "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 }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "has-flag": "^3.0.0" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + } + } + }, + "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 + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "prompts": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "dependencies": { + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + } + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, - "optional": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" } } } }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", @@ -6720,9 +18920,9 @@ } }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, "remove-trailing-separator": { @@ -6733,9 +18933,9 @@ "optional": true }, "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true, "optional": true }, @@ -6754,65 +18954,18 @@ "is-finite": "^1.0.0" } }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "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.3", - "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.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "request-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", - "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", - "requires": { - "bluebird": "^3.5.0", - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "requires": { - "lodash": "^4.17.19" - }, - "dependencies": { - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - } - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "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", @@ -6829,12 +18982,14 @@ } }, "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-from": { @@ -6850,16 +19005,6 @@ "dev": true, "optional": true }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -6867,6 +19012,12 @@ "dev": true, "optional": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -6895,28 +19046,29 @@ "inherits": "^2.0.1" } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "rollup": { + "version": "2.70.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz", + "integrity": "sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==", "dev": true, "requires": { - "is-promise": "^2.1.0" + "fsevents": "~2.3.2" } }, - "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { - "tslib": "^1.9.0" + "queue-microtask": "^1.2.2" } }, "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==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -6931,7 +19083,8 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "sanitize-html": { "version": "1.20.0", @@ -6982,12 +19135,62 @@ } } }, + "selfsigned": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", + "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -7019,6 +19222,12 @@ } } }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, "sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", @@ -7066,10 +19275,32 @@ "jsonify": "~0.0.0" } }, + "shiki": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", + "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "dev": true, + "requires": { + "jsonc-parser": "^3.0.0", + "vscode-oniguruma": "^1.6.1", + "vscode-textmate": "5.2.0" + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "simple-concat": { @@ -7078,33 +19309,17 @@ "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", "dev": true }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "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" - } - }, - "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 - } - } + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true }, "snapdragon": { "version": "0.8.2", @@ -7199,17 +19414,10 @@ "kind-of": "^6.0.2" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "optional": true } @@ -7231,14 +19439,20 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "dev": true, "optional": true, "requires": { - "atob": "^2.1.1", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -7246,44 +19460,12 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true, "optional": 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 - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -7310,22 +19492,6 @@ "number-is-nan": "^1.0.0" } }, - "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" - } - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -7349,10 +19515,11 @@ } } }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true }, "stream-browserify": { "version": "2.0.2", @@ -7397,6 +19564,15 @@ "readable-stream": "^2.0.2" } }, + "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==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -7408,13 +19584,24 @@ "strip-ansi": "^3.0.0" } }, - "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==", + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "strip-ansi": { @@ -7454,9 +19641,9 @@ }, "dependencies": { "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true } } @@ -7467,6 +19654,12 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "syntax-error": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", @@ -7476,64 +19669,6 @@ "acorn-node": "^1.2.0" } }, - "table": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.5.tgz", - "integrity": "sha512-oGa2Hl7CQjfoaogtrOHEJroOcYILTx7BZWLGsJIlzoWmB2zmguhNfPJZsWPKYek/MgCxfco54gEi31d1uN2hFA==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", @@ -7571,15 +19706,6 @@ "process": "~0.11.0" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -7624,28 +19750,13 @@ "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - } - } } }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true }, "trim-right": { "version": "1.0.1", @@ -7653,6 +19764,26 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tsconfig-paths": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz", + "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + } + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -7660,9 +19791,9 @@ "dev": true }, "tsutils": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.1.tgz", - "integrity": "sha512-kiuZzD1uUA5DxGj/uxbde+ymp6VVdAxdzOIlAFbYKrPyla8/uiJ9JLBm1QsPhOm4Muj0/+cWEDP99yoCUcSl6Q==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -7674,26 +19805,13 @@ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", "dev": true }, - "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.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "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.1.2" + "prelude-ls": "^1.2.1" } }, "type-detect": { @@ -7702,16 +19820,98 @@ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", "dev": true }, + "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 + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typedoc": { + "version": "0.22.13", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.13.tgz", + "integrity": "sha512-NHNI7Dr6JHa/I3+c62gdRNXBIyX7P33O9TafGLd07ur3MqzcKgwTvpg18EtvCLHJyfeSthAtCLpM7WkStUmDuQ==", + "dev": true, + "requires": { + "glob": "^7.2.0", + "lunr": "^2.3.9", + "marked": "^4.0.12", + "minimatch": "^5.0.1", + "shiki": "^0.10.1" + }, + "dependencies": { + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "marked": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", + "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "dev": true + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + } + } + }, "typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", - "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", + "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", "dev": true }, "uc.micro": { @@ -7721,9 +19921,10 @@ "dev": true }, "uglify-js": { - "version": "git://github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd", - "from": "git://github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd", + "version": "git+ssh://git@github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd", + "integrity": "sha512-Hp6wHD1vnDyGp4oG97X9VO4Ts3Xb0RYrOTNSWZu0a+/zxlHQGl4ENDrWRjHoOZs647ZLZiBterS1plKGAqsSHQ==", "dev": true, + "from": "uglify-js@git://github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd", "requires": { "async": "~0.2.6", "source-map": "~0.5.1", @@ -7736,26 +19937,77 @@ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "decamelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz", + "integrity": "sha1-UocSL3FpHUUFsY/yJY3EAKWyOEc=", + "dev": true + }, + "source-map": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.1.tgz", + "integrity": "sha1-X71Aoc3khf6rTTZGC49Jbts2Ls8=", + "dev": true + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true - }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - }, "umd": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + } + } + }, "undeclared-identifiers": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", @@ -7770,9 +20022,9 @@ } }, "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==", "dev": true }, "union-value": { @@ -7794,6 +20046,12 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -7835,20 +20093,13 @@ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true, "optional": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true } } }, "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true, "optional": true }, @@ -7856,6 +20107,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, "requires": { "punycode": "^2.1.0" } @@ -7892,6 +20144,16 @@ "dev": true, "optional": true }, + "utf-8-validate": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.9.tgz", + "integrity": "sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q==", + "optional": true, + "peer": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "util": { "version": "0.10.4", "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", @@ -7907,35 +20169,48 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true }, "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "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" - } + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "vite": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.1.tgz", + "integrity": "sha512-vSlsSdOYGcYEJfkQ/NeLXgnRv5zZfpAsdztkIrs7AZHV8RCMZQkwjo4DS5BnrYTqoWqLoUe1Cah4aVO4oNNqCQ==", + "dev": true, "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "esbuild": "^0.14.27", + "fsevents": "~2.3.2", + "postcss": "^8.4.12", + "resolve": "^1.22.0", + "rollup": "^2.59.0" + }, + "dependencies": { + "postcss": { + "version": "8.4.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.12.tgz", + "integrity": "sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==", + "dev": true, + "requires": { + "nanoid": "^3.3.1", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + } } }, "vm-browserify": { @@ -7947,6 +20222,18 @@ "indexof": "0.0.1" } }, + "vscode-oniguruma": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz", + "integrity": "sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==", + "dev": true + }, + "vscode-textmate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", + "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "dev": true + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -7956,6 +20243,36 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + } + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -7971,10 +20288,10 @@ "string-width": "^1.0.2 || 2" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "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": { @@ -7999,29 +20316,16 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "requires": {} }, "xmlcreate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.1.tgz", - "integrity": "sha512-MjGsXhKG8YjTKrDCXseFo3ClbMGvUD4en29H2Cev1dv4P/chlpw6KdYmlCWDkhosBVKRDjM836+3e3pm1cBNJA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", + "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", "dev": true }, "xtend": { @@ -8036,35 +20340,11 @@ "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - }, - "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "yargs-unparser": { "version": "1.5.0", diff --git a/package.json b/package.json index db9e2d71..ab23f095 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "snoowrap", - "version": "1.23.0", + "version": "2.0.0", "license": "MIT", "description": "A JavaScript wrapper for the reddit API", "main": "dist/snoowrap.js", @@ -9,18 +9,22 @@ "prepublishOnly": "npm run compile", "prepare": "npm run compile", "compile": "npm run type-check && npm run build:babel && npm run build:types && node ./scripts/copyTSTypes.js", - "lint": "eslint --ignore-path .gitignore . --cache", + "lint": "eslint . --ext .ts,.tsx --ignore-path .gitignore --cache", "test": "npm run lint && npm run test:mocha", "test:mocha": "npm run compile && mocha --require @babel/register test/snoowrap.spec.js", - "test:browser": "npm run compile && browserify --im test/snoowrap.spec.js -o test/_browser.spec.js -t [ babelify ] && open test/run-tests.html", + "test:browser": "npm run compile && browserify --im test/snoowrap.spec.js -o test/_browser.spec.js -t [ babelify ] && five-server --ignore=** --open=/test/run-tests.html", "smoketest": "npm run test -- -g 'smoketest'", + "browser": "npm run compile && browserify --im browser/index.js -o browser/_index.js -t [ babelify ] && five-server --ignore=** --open=/browser/index.html", "build-docs": "npm run build:docs", "build:docs": "scripts/build_docs.sh", - "build:babel": "babel -q -d dist/ src/", + "build:babel": "babel -d dist/ src/", "build:types": "tsc -p tsconfig.gen-dts.json", "bundle-size": "npm run build-docs && gzip -c doc/snoowrap-v$(npm info . version).min.js | wc -c | xargs", "coverage": "istanbul cover _mocha -- --require @babel/register test/snoowrap.spec.js", - "type-check": "tsc -p tsconfig.typecheck.json" + "type-check": "tsc -p tsconfig.typecheck.json", + "docs": "typedoc --out docs --entryPointStrategy expand src --sort source-order", + "dev": "vite dev", + "bui": "vite build" }, "files": [ "dist/" @@ -42,15 +46,14 @@ }, "homepage": "https://github.com/not-an-aardvark/snoowrap", "dependencies": { - "bluebird": "^3.5.5", + "axios": "^0.21.1", + "form-data": "^4.0.0", "lodash": "^4.17.15", - "promise-chains": "^0.3.11", - "request": "^2.88.2", - "request-promise": "^4.2.6", - "ws": "^3.3.1" + "path-browserify": "^1.0.1", + "ws": "^8.5.0" }, "devDependencies": { - "@babel/cli": "^7.0.0", + "@babel/cli": "^7.14.8", "@babel/core": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", "@babel/plugin-transform-arrow-functions": "^7.0.0", @@ -63,32 +66,38 @@ "@babel/plugin-transform-template-literals": "^7.0.0", "@babel/plugin-transform-typescript": "^7.0.0", "@babel/register": "^7.5.5", + "@types/lodash": "^4.14.179", + "@types/path-browserify": "^1.0.0", "@types/request": "^2.48.2", - "@typescript-eslint/eslint-plugin": "^1.13.0", - "@typescript-eslint/parser": "^1.13.0", + "@types/ws": "^8.5.3", + "@typescript-eslint/eslint-plugin": "^5.15.0", + "@typescript-eslint/parser": "^5.15.0", "babelify": "^10.0.0", "browserify": "^13.1.0", "chai": "^3.5.0", "dirty-chai": "^1.2.2", - "eslint": "^6.1.0", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-promise": "^4.2.1", + "eslint": "^8.11.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-promise": "^6.0.0", + "five-server": "0.0.28", "fs-extra": "^8.1.0", "ink-docstrap": "^1.2.1", "istanbul": "^1.0.0-alpha.2", - "jsdoc": "^3.6.3", + "jsdoc": "^3.6.7", "mocha": "^6.0.0", "moment": "^2.14.1", - "typescript": "^3.5.3", - "uglify-js": "git://github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd" + "typedoc": "^0.22.13", + "typescript": "^4.6.2", + "uglify-js": "git://github.com/mishoo/UglifyJS2.git#1db50c3b169ee4195e1935013d6721628eb5b4bd", + "vite": "^2.9.1" }, "engines": { "node": ">=4.0.0" }, "browser": { "fs": false, - "request-promise": false, "util": false, - "ws": false + "ws": false, + "form-data": false } } diff --git a/scripts/build_docs.sh b/scripts/build_docs.sh index 781130e7..674aa6ee 100755 --- a/scripts/build_docs.sh +++ b/scripts/build_docs.sh @@ -16,7 +16,7 @@ mkdir -p doc npm run compile # List all the files explicitly rather than globbing to ensure that the classes appear in the right order in the docs -node_modules/.bin/jsdoc -c jsdoc.conf.json dist/snoowrap.js dist/request_handler.js dist/objects/RedditContent.js dist/objects/ReplyableContent.js dist/objects/VoteableContent.js dist/objects/Comment.js dist/objects/RedditUser.js dist/objects/Submission.js dist/objects/LiveThread.js dist/objects/PrivateMessage.js dist/objects/Subreddit.js dist/objects/MultiReddit.js dist/objects/ModmailConversation.js dist/objects/ModmailConversationAuthor.js dist/objects/WikiPage.js dist/objects/Listing.js +node_modules/.bin/jsdoc -c jsdoc.conf.json dist/snoowrap.js dist/objects/RedditContent.js dist/objects/ReplyableContent.js dist/objects/VoteableContent.js dist/objects/Comment.js dist/objects/RedditUser.js dist/objects/Submission.js dist/objects/LiveThread.js dist/objects/PrivateMessage.js dist/objects/Subreddit.js dist/objects/MultiReddit.js dist/objects/ModmailConversation.js dist/objects/ModmailConversationAuthor.js dist/objects/WikiPage.js dist/objects/Listing.js # Create the bundle files, e.g. 'snoowrap-v1.2.3.js' and 'snoowrap-v1.2.3.min.js' node_modules/.bin/browserify dist/snoowrap.js -o "doc/snoowrap-$FULL_VERSION.js" # Exclude snoowrap's class names from mangling. diff --git a/src/BaseRequester.ts b/src/BaseRequester.ts new file mode 100644 index 00000000..2a36daa0 --- /dev/null +++ b/src/BaseRequester.ts @@ -0,0 +1,539 @@ +import axiosCreate from './axiosCreate' +import {isBrowser, requiredArg} from './helpers' +import defaultConfig from './defaultConfig' +import {GRANT_TYPES, DEVICE_ID, IDEMPOTENT_HTTP_VERBS, MAX_TOKEN_LATENCY} from './constants' +import {rateLimitWarning, RateLimitError} from './errors' +import type {AxiosRequestConfig, AxiosResponse, AxiosError} from './axiosCreate' +import type {CredentialsResponse} from './interfaces' + + +interface Common { + redirect_uri?: string + user_agent?: string + device_id?: string + grant_type?: string + config?: typeof defaultConfig +} + +interface AppAuth extends Common { + client_id: string + client_secret?: string + refresh_token?: string + access_token?: string +} + +interface ScriptAuth extends Common { + client_id: string + client_secret: string + username: string + password: string + two_factor_code?: number | string + access_token?: string +} + +interface CodeAuth extends Common { + client_id: string + client_secret?: string + code: string + redirect_uri: string +} + +interface All extends Common, Partial, Partial, Partial {} + +/** + * The BaseRequester class. + * This is an internal class that is responsible for authentication stuff, sending API requests to Reddit, + * and other basic functionalities needed by snoowrap. + * + * This class neither contains API methods nor transforms API responses into populated objects. + * Use {@link snoowrap} instead. + */ +interface BaseRequester extends All {} +class BaseRequester { + scope?: string[] + tokenExpiration!: number + ratelimitRemaining!: number + ratelimitExpiration!: number + _nextRequestTimestamp!: number + _config = {...defaultConfig} + + constructor (options?: AppAuth) + constructor (options?: ScriptAuth) + constructor (options?: CodeAuth) + constructor (options?: Common) + constructor (options: All = {}) { + this.setOptions(options) + } + + /** + * @summary A method use to set/update requester options + */ + setOptions (options: All) { + this.client_id = options.client_id + this.client_secret = options.client_secret || '' + this.refresh_token = options.refresh_token + this.access_token = options.access_token + this.username = options.username + this.password = options.password + this.two_factor_code = options.two_factor_code + this.code = options.code + this.redirect_uri = options.redirect_uri + this.user_agent = isBrowser ? self.navigator.userAgent : options.user_agent || requiredArg('user_agent') + this.device_id = options.device_id || DEVICE_ID + this.grant_type = options.grant_type || BaseRequester.grantTypes.INSTALLED_CLIENT + this.scope = undefined + this.tokenExpiration = Infinity + this.ratelimitRemaining = Infinity + this.ratelimitExpiration = Infinity + this._nextRequestTimestamp = -Infinity + this._config = { + ...defaultConfig, + ...options.config + } + } + + /** + * @summary Returns the grant types available for authentication + * @returns The enumeration of possible grant_type values + */ + static get grantTypes () { + return {...GRANT_TYPES} + } + + /** + * @summary An internal method to objectify reddit api responses + */ + _populate (response: AxiosResponse) { + return response + } + + /** + * @summary An internal method used to log warnings + */ + _warn (...args: any[]) { + if (this._config.warnings) { + this._config.logger.warn(...args) + } + } + + /** + * @summary An internal method used to log debug messages + */ + _debug (...args: any[]) { + if (this._config.debug) { + this._config.logger.debug(...args) + } + } + + /** + * @summary Gets an authorization URL, which allows a user to authorize access to their account + * @desc This create a URL where a user can authorize an app to act through their account. If the user visits the returned URL + * in a web browser, they will see a page that looks like [this](https://i.gyazo.com/0325534f38b78c1dbd4c84d690dda6c2.png). If + * the user clicks "Allow", they will be redirected to your `redirect_uri`, with a `code` querystring parameter containing an + * *authorization code*. If this code is passed to {@link BaseRequester}, you can make requests on behalf of the user. + * + * The main use-case here is for running snoowrap in a browser. You can generate a URL, send the user there, and then continue + * after the user authenticates on reddit and is redirected back. + * + * @param options + * @param options.client_id The client ID of your app (assigned by reddit). If your code is running clientside in a + * browser, using an "Installed" app type is recommended. + * @param options.scope An array of scopes (permissions on the user's account) to request on the authentication + * page. A list of possible scopes can be found [here](https://www.reddit.com/api/v1/scopes). You can also get them on-the-fly + * with {@link snoowrap#getOauthScopeList}. Passing an array with a single asterisk `['*']` gives you full scope. + * @param options.redirect_uri The URL where the user should be redirected after authenticating. This **must** be the + * same as the redirect URI that is configured for the reddit app. (If there is a mismatch, the returned URL will display an + * error page instead of an authentication form.) + * @param options.permanent If `true`, the app will have indefinite access to the user's account. If `false`, + * access to the user's account will expire after 1 hour. + * @param options.state A string that can be used to verify a user after they are redirected back to the site. When + * the user is redirected from reddit, to the redirect URI after authenticating, the resulting URI will have this same `state` + * value in the querystring. (See [here](http://www.twobotechnologies.com/blog/2014/02/importance-of-state-in-oauth2.html) for + * more information on how to use the `state` value.) + * @param options.compact If `true`, the mobile version of the authorization URL will be used instead. + * @returns A URL where the user can authenticate with the given options + * @example + * + * const authenticationUrl = r.getAuthUrl({ + * scope: ['identity', 'wikiread', 'wikiedit'], + * redirect_uri: 'https://example.com/reddit_callback', + * permanent: false, + * state: 'fe211bebc52eb3da9bef8db6e63104d3' // a random string, this could be validated when the user is redirected back + * }); + * // --> 'https://www.reddit.com/api/v1/authorize?client_id=foobarbaz&response_type=code&state= ...' + * + * window.location.href = authenticationUrl; // send the user to the authentication url + */ + getAuthUrl ({ + client_id = (this.client_id || requiredArg('client_id')) as string, + scope = ['*'], + redirect_uri = (this.redirect_uri || requiredArg('redirect_uri')) as string, + permanent = true, + state = '_', + compact = false + }) { + if (!(Array.isArray(scope) && scope.length && scope.every(scopeValue => scopeValue && typeof scopeValue === 'string'))) { + throw new TypeError('Missing `scope` argument; a non-empty list of OAuth scopes must be provided'); + } + return [ + `https://www.${this._config.endpointDomain}/api/v1/authorize`, + `${compact ? '.compact' : ''}`, + `?client_id=${encodeURIComponent(client_id)}`, + '&response_type=code', + `&state=${encodeURIComponent(state)}`, + `&redirect_uri=${encodeURIComponent(redirect_uri)}`, + `&duration=${permanent ? 'permanent' : 'temporary'}`, + `&scope=${encodeURIComponent(scope.join(' '))}` + ].join('') + } + + /** + * @summary Sends an oauth-authenticated request to the reddit server, and returns the server's response. + * @desc **Note**: While this function primarily exists for internal use, it is exposed and considered a stable feature. + * However, keep in mind that there are usually better alternatives to using this function. For instance, this + * function can be used to send a POST request to the 'api/vote' endpoint in order to upvote a comment, but it's generally + * easier to just use snoowrap's [upvote function]{@link VoteableContent#upvote}. + * + * If you're using this function to access an API feature/endpoint that is unsupported by snoowrap, please consider [creating an + * issue for it](https://github.com/not-an-aardvark/snoowrap/issues) so that the functionality can be added to snoowrap more + * directly. + * @param options Options for the request. See {@link snoowrap#rawRequest} for more details. A default `baseURL` parameter + * of `this._config.endpoint_domain` is internally included by default, so it is recommended that a relative `url` parameter be used, + * rather than an absolute `url` parameter with a domain name. + * @returns A Promise that fulfills with reddit's response. + * @memberof snoowrap + * @instance + * @example + * + * r.oauthRequest({url: '/user/spez/about', method: 'get'}).then(console.log) + * // => RedditUser { name: 'spez', link_karma: 9567, ... } + * + * // Note that this is equivalent to: + * r.getUser('spez').fetch().then(console.log) + * + * // ###### + * + * r.oauthRequest({url: '/api/vote', method: 'post', form: {dir: 1, id: 't3_4fzg2k'}}) + * // equivalent to: + * r.getSubmission('4fzg2k').upvote() + * + * // ###### + * + * r.oauthRequest({url: '/top', method: 'get', params: {t: 'all'}}) + * // equivalent to: + * r.getTop({time: 'all'}) + */ + async oauthRequest (config: AxiosRequestConfig, attempts = 1): Promise { + try { + await this._awaitRatelimit() + await this._awaitRequestDelay() + await this._awaitExponentialBackoff(attempts) + + const token = await this.updateAccessToken() + + const response = await this.axiosCreate({ + baseURL: `https://oauth.${this._config.endpointDomain}`, + headers: { + authorization: `Bearer ${token}`, + 'user-agent': this.user_agent! + }, + params: { + raw_json: 1 + }, + timeout: this._config.requestTimeout, + _r: this + })(config) + + if (response.headers['x-ratelimit-remaining']) { + this.ratelimitRemaining = Number(response.headers['x-ratelimit-remaining']) + this.ratelimitExpiration = Date.now() + (Number(response.headers['x-ratelimit-reset']) * 1000) + } + + this._debug( + `Received a ${response.status} status code from a \`${response.config.method}\` request` + + `sent to ${response.config.url}. ratelimitRemaining: ${this.ratelimitRemaining}` + ) + + return this._populate(response) + } catch (e) { + const err = e + this._debug('Error:', {err}) + + if ( + err.response && + this._config.retryErrorCodes.some(retryCode => retryCode === err.response!.status) && + IDEMPOTENT_HTTP_VERBS.some(verb => verb === err.config.method) && + attempts < this._config.maxRetryAttempts + ) { + /** + * If the error's status code is in the user's configured `retryStatusCodes` and this request still has attempts + * remaining, retry this request and increment the `attempts` counter. + */ + this._warn( + `Received status code ${err.response.status} from reddit.`, + `Retrying request (attempt ${attempts + 1}/${this._config.maxRetryAttempts})...` + ) + return this.oauthRequest(config, attempts + 1) + } else if ( + err.response && + err.response.status === 401 && + this.access_token && + this.tokenExpiration - Date.now() < MAX_TOKEN_LATENCY + ) { + /** + * If the server returns a 401 error, it's possible that the access token expired during the latency period as this + * request was being sent. In this scenario, snoowrap thought that the access token was valid for a few more seconds, so it + * didn't refresh the token, but the token had expired by the time the request reached the server. To handle this issue, + * invalidate the access token and call oauth_request again, automatically causing the token to be refreshed. + */ + this.tokenExpiration = -Infinity + return this.oauthRequest(config, attempts) + } + throw err + } + } + + _awaitRatelimit () { + if (this.ratelimitRemaining < 1 && Date.now() < this.ratelimitExpiration) { + // If the ratelimit has been exceeded, delay or abort the request depending on the user's config. + if (this._config.continueAfterRatelimitError) { + /** + * If the `continue_after_ratelimit_error` setting is enabled, queue the request, wait until the next ratelimit + * period, and then send it. + */ + const timeUntilExpiry = this.ratelimitExpiration - Date.now() + this._warn(rateLimitWarning(timeUntilExpiry)) + return new Promise(resolve => setTimeout(resolve, timeUntilExpiry)) + } + // Otherwise, throw an error. + throw new RateLimitError() + } + // If the ratelimit hasn't been exceeded, no delay is necessary. + } + + _awaitRequestDelay () { + const now = Date.now() + const waitTime = this._nextRequestTimestamp - now + this._nextRequestTimestamp = Math.max(now, this._nextRequestTimestamp) + this._config.requestDelay + return new Promise(resolve => setTimeout(resolve, waitTime)) + } + + _awaitExponentialBackoff (attempts: number) { + if (attempts === 1) { + return + } + const waitTime = (Math.pow(2, attempts - 1) + (Math.random() - 0.3)) * 1000 + return new Promise(resolve => setTimeout(resolve, waitTime)) + } + + /** + * @summary Sends a request to the reddit server, authenticated with the user's client ID and client secret. + * @desc **Note**: This is used internally as part of the authentication process, but it cannot be used to actually fetch + * content from reddit. To do that, use {@link snoowrap#oauthRequest} or another of snoowrap's helper functions. + * + * This function can work with alternate `this`-bindings, provided that the binding has the `clientId`, `clientSecret`, and + * `userAgent` properties. This allows it be used if no snoowrap requester has been created yet. + * @param options Options for the request; See {@link snoowrap#rawRequest} for more details. + * @returns The response from the reddit server + * @example + * + * // example: this function could be used to exchange a one-time authentication code for a refresh token. + * snoowrap.prototype.credentialedClientRequest.call({ + * clientId: 'client id goes here', + * clientSecret: 'client secret goes here', + * userAgent: 'user agent goes here' + * }, { + * method: 'post', + * baseURL: 'https://www.reddit.com', + * url: 'api/v1/access_token', + * form: {grant_type: 'authorization_code', code: 'code goes here', redirect_uri: 'redirect uri goes here'} + * }).then(response => { + * //handle response here + * }) + * @memberof snoowrap + * @instance + */ + credentialedClientRequest (config: AxiosRequestConfig) { + if (!this.client_id) { + throw new Error('Missing access_token') + } + return this.axiosCreate({ + baseURL: `https://www.${this._config.endpointDomain}`, + headers: { + 'user-agent': this.user_agent! + }, + auth: { + username: this.client_id!, + password: this.client_secret! + }, + timeout: this._config.requestTimeout, + _r: this + }).request(config) + } + + /** + * @summary Updates this requester's access token if the current one is absent or expired. + * @desc **Note**: This function is automatically called internally when making a request. While the function is exposed as + * a stable feature, using it is rarely necessary unless an access token is needed for some external purpose, or to test + * the validity of the refresh token. + * @returns A Promise that fulfills with the access token when this request is complete + * @memberof snoowrap + * @instance + * @example r.updateAccessToken() + */ + async updateAccessToken () { + if (!this.access_token || Date.now() > this.tokenExpiration) { + let form: AxiosRequestConfig['form'] + + if (this.refresh_token) { + form = { + grant_type: BaseRequester.grantTypes.REFRESH_TOKEN, + refresh_token: this.refresh_token + } + } else if (this.username && this.password) { + const password = this.two_factor_code ? `${this.password}:${this.two_factor_code}` : this.password + form = { + grant_type: BaseRequester.grantTypes.PASSWORD, + username: this.username, + password + } + } else if (this.code && this.redirect_uri) { + form = { + grant_type: BaseRequester.grantTypes.AUTHORIZATION_CODE, + code: this.code, + redirect_uri: this.redirect_uri + } + } else if (this.grant_type && this.device_id) { // fallback + form = { + grant_type: this.grant_type, + device_id: this.device_id + } + } + + const response = await this.credentialedClientRequest({ + method: 'post', + url: 'api/v1/access_token', + form + }) + + const { + access_token, + refresh_token, + expires_in, + scope, + error, + error_description + }: CredentialsResponse = response.data + + if (error) throw new Error(error_description ? `${error} - ${error_description}` : error) + + this.access_token = access_token + this.refresh_token = refresh_token + this.tokenExpiration = Date.now() + (expires_in * 1000) + this.scope = scope.split(' ') + } + return this.access_token + } + + /** + * @summary Invalidates the given API token + * @returns A Promise that fulfills when this request is complete + */ + revokeToken (token: string) { + return this.credentialedClientRequest({url: 'api/v1/revoke_token', form: {token}, method: 'post'}) + } + + /** + * @summary Invalidates the current access token. + * @returns A Promise that fulfills when this request is complete + * @desc **Note**: This can only be used if the current requester was supplied with a `client_id` and `client_secret`. If the + * current requester was supplied with a refresh token, it will automatically create a new access token if any more requests + * are made after this one. + * @example r.revokeAccessToken(); + */ + async revokeAccessToken () { + if (!this.access_token) { + throw new Error('Missing access_token') + } + await this.revokeToken(this.access_token) + this.access_token = undefined + this.tokenExpiration = Infinity + this.scope = undefined + } + + /** + * @summary Invalidates the current refresh token. + * @returns {Promise} A Promise that fulfills when this request is complete + * @desc **Note**: This can only be used if the current requester was supplied with a `client_id` and `client_secret`. All + * access tokens generated by this refresh token will also be invalidated. This effectively de-authenticates the requester and + * prevents it from making any more valid requests. This should only be used in a few cases, e.g. if this token has + * been accidentally leaked to a third party. + * @example r.revokeRefreshToken(); + */ + async revokeRefreshToken () { + if (!this.refresh_token) { + throw new Error('Missing refresh_token') + } + await this.revokeToken(this.refresh_token) + this.refresh_token = undefined + this.access_token = undefined // Revoking a refresh token also revokes any associated access tokens. + this.tokenExpiration = Infinity + this.scope = undefined + } + + /** + * @summary Sends a request to the reddit server without authentication. + * @param options Options for the request + * @returns {Promise} The response from the reddit server + * @memberof snoowrap + * @instance + */ + unauthenticatedRequest (config: AxiosRequestConfig) { + return this.axiosCreate({ + baseURL: 'https://www.reddit.com', + headers: { + 'user-agent': this.user_agent + }, + params: { + raw_json: 1 + }, + timeout: this._config.requestTimeout, + _r: this + })(config) + } + + get request () { + return this.client_id || this.access_token ? this.oauthRequest : this.unauthenticatedRequest + } + + _get (config: AxiosRequestConfig) { + config.method = 'GET' + return this.request(config) + } + _post (config: AxiosRequestConfig) { + config.method = 'POST' + return this.request(config) + } + _put (config: AxiosRequestConfig) { + config.method = 'PUT' + return this.request(config) + } + _delete (config: AxiosRequestConfig) { + config.method = 'DELETE' + return this.request(config) + } + _head (config: AxiosRequestConfig) { + config.method = 'HEAD' + return this.request(config) + } + _patch (config: AxiosRequestConfig) { + config.method = 'PATCH' + return this.request(config) + } + + axiosCreate = axiosCreate + rawRequest = this.axiosCreate() +} + +export default BaseRequester +export {Common, AppAuth, ScriptAuth, CodeAuth, All} diff --git a/src/Promise.js b/src/Promise.js deleted file mode 100644 index 22967b3d..00000000 --- a/src/Promise.js +++ /dev/null @@ -1,5 +0,0 @@ -import Promise from 'bluebird'; - -const PromiseCopy = Promise.getNewLibraryCopy(); -PromiseCopy.config({cancellation: true, warnings: false}); -export default PromiseCopy; diff --git a/src/README.md b/src/README.md index 5ef2cf6c..e1f3c86b 100644 --- a/src/README.md +++ b/src/README.md @@ -41,6 +41,7 @@ A few other useful commands: npm run lint # runs only the linter npm run test:browser # runs the unit tests in your browser npm run smoketest # runs two tests and then stops. This is useful to make sure your setup is correct. +npm run browser # to test and debug snoowrap on browser console manually. npm run compile # compiles the source code using babel. This automatically gets run before the tests are run, but it's useful if you want to use `require('.')` in the node REPL. npm run build-docs # builds the documentation into a doc/ folder ``` @@ -94,20 +95,20 @@ The base class for a snoowrap requester is found in `snoowrap.js`. There are a f // --> send a GET request to reddit.com/r/snoowrap/about/moderators, and // return a Promise for the populated response -r._get({uri: 'r/snoowrap/about/moderators'}) +r._get({url: 'r/snoowrap/about/moderators'}) // --> send a POST request to reddit.com/api/remove with form data {id: 't3_2np694'} -r._post({uri: 'api/remove', form: {id: 't3_2np694'}}) +r._post({url: 'api/remove', form: {id: 't3_2np694'}}) ``` -The helper functions are `_get`, `_post`, `_put`, `_delete`, etc., corresponding to all the HTTP verbs. Parameters from these functions are passed directly to [request-promise](https://github.com/request/request-promise), so all of the request options from the [request API](https://www.npmjs.com/package/request) are easily available. Note that `request_handlers.js` already provides the following default options: +The helper functions are `_get`, `_post`, `_put`, `_delete`, etc., corresponding to all the HTTP verbs. Parameters from these functions are passed directly to [axios](https://www.npmjs.com/package/axios), so all of the request options from the [axios API](https://www.npmjs.com/package/axios) are easily available in addition of snoowrap specific options such as `formData` and `form` (check `snoowrap#rawRequest` for more details). Note that `request_handlers.js` already provides the following default options: ```js { auth: {bearer: "(the user's access token)"}, headers: {'user-agent': "(the user's user-agent string)"}, - baseUrl: "the user's specified endpoint domain, usually https://oauth.reddit.com", - qs: {raw_json: 1} // (prevents reddit from escaping HTML characters), + baseURL: "the user's specified endpoint domain, usually https://oauth.reddit.com", + params: {raw_json: 1} // (prevents reddit from escaping HTML characters), timeout: "(the user's timeout)" } ``` diff --git a/src/axiosCreate.ts b/src/axiosCreate.ts new file mode 100644 index 00000000..23fb7641 --- /dev/null +++ b/src/axiosCreate.ts @@ -0,0 +1,68 @@ +declare module 'axios' { + export interface AxiosRequestConfig { + form?: { + [key: string]: any + } + formData?: { + [key: string]: any + } + _r?: any + } +} + +import axios from 'axios' +import {URLSearchParams, FormData, isBrowser} from './helpers' +import type {AxiosRequestConfig, AxiosResponse, AxiosError} from 'axios' + + +function axiosCreate (baseConfig?: AxiosRequestConfig) { + const instance = axios.create(baseConfig) + + instance.interceptors.request.use(async config => { + if (config.formData) { + const requestBody = new FormData() + for (const key of Object.keys(config.formData)) { + const value = config.formData[key] + if (value !== undefined) requestBody.append(key, config.formData[key]) + } + if (!isBrowser) { + const contentLength: number = await new Promise((resolve, reject) => { + requestBody.getLength((err, length) => { + if (err) reject(err) + resolve(length) + }) + }) + config.headers!['content-length'] = contentLength + config.headers!['content-type'] = `multipart/form-data; boundary=${requestBody.getBoundary()}` + } + config.data! = requestBody + } else if (config.form) { + const requestBody = new URLSearchParams() + for (const key of Object.keys(config.form)) { + const value = config.form[key] + if (value !== undefined) requestBody.append(key, value) + } + config.data = requestBody.toString() + config.headers['content-type'] = 'application/x-www-form-urlencoded' + } + if (isBrowser) { + delete config.headers['user-agent'] + } + if (config._r && config._r._debug) { + config._r._debug('Request:', config) + } + return config + }) + + instance.interceptors.response.use(response => { + if (response.config._r && response.config._r._debug) { + response.config._r._debug('Response:', response) + } + return response + }) + + return instance +} + +export default axiosCreate +export {AxiosRequestConfig, AxiosResponse, AxiosError} diff --git a/src/constants.js b/src/constants.js deleted file mode 100644 index db106960..00000000 --- a/src/constants.js +++ /dev/null @@ -1,43 +0,0 @@ -export const MODULE_NAME = 'snoowrap'; -export const VERSION = '1.23.0'; -export const DOCS_LINK = 'https://not-an-aardvark.github.io/snoowrap/'; -export const API_RULES_LINK = 'https://github.com/reddit/reddit/wiki/API'; -/* USER_KEYS and SUBREDDIT_KEYS are keys that are replaced by RedditUser and Subreddit objects when encountered in -`snoowrap#_populate`. `author`, `approved_by`, `banned_by`, and `subreddit` all appear in fetched Submissions, among other -places. `user` appears in responses from the api/flairlist endpoint, and `sr` appears in responses from the `api/v1/me/karma` -endpoint. */ -export const USER_KEYS = new Set(['author', 'approved_by', 'banned_by', 'user']); -export const SUBREDDIT_KEYS = new Set(['subreddit', 'sr']); -export const KINDS = { - t1: 'Comment', - t2: 'RedditUser', - t3: 'Submission', - t4: 'PrivateMessage', - t5: 'Subreddit', - t6: 'Trophy', - t8: 'PromoCampaign', - Listing: 'Listing', - more: 'More', - UserList: 'UserList', - KarmaList: 'KarmaList', - TrophyList: 'TrophyList', - subreddit_settings: 'SubredditSettings', - modaction: 'ModAction', - wikipage: 'WikiPage', - wikipagesettings: 'WikiPageSettings', - wikipagelisting: 'WikiPageListing', - LiveUpdateEvent: 'LiveThread', - LiveUpdate: 'LiveUpdate', - LabeledMulti: 'MultiReddit', - ModmailConversation: 'ModmailConversation', - ModmailConversationAuthor: 'ModmailConversationAuthor' -}; -export const USERNAME_REGEX = /^[\w-]{1,20}$/; -export const MODERATOR_PERMISSIONS = ['wiki', 'posts', 'access', 'mail', 'config', 'flair']; -export const LIVETHREAD_PERMISSIONS = ['update', 'edit', 'manage']; -export const HTTP_VERBS = ['delete', 'get', 'head', 'patch', 'post', 'put']; -export const IDEMPOTENT_HTTP_VERBS = ['delete', 'get', 'head', 'put']; -export const MAX_TOKEN_LATENCY = 10000; -export const MAX_API_INFO_AMOUNT = 100; -export const MAX_API_MORECHILDREN_AMOUNT = 20; -export const MAX_LISTING_ITEMS = 100; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 00000000..b2bdfad2 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,71 @@ +export const MODULE_NAME = 'snoowrap' +export const VERSION = '2.0.0' +export const DOCS_LINK = 'https://not-an-aardvark.github.io/snoowrap/' +export const API_RULES_LINK = 'https://github.com/reddit/reddit/wiki/API' +/** + * USER_KEYS and SUBREDDIT_KEYS are keys that are replaced by RedditUser and Subreddit objects when encountered in + * `snoowrap#_populate`. `author`, `approved_by`, `banned_by`, and `subreddit` all appear in fetched Submissions, among other + * places. `user` appears in responses from the api/flairlist endpoint, and `sr` appears in responses from the `api/v1/me/karma` + * endpoint. + */ +export const USER_KEYS = new Set(['author', 'approved_by', 'banned_by', 'user']) +export const SUBREDDIT_KEYS = new Set(['subreddit', 'sr']) +export const KINDS = { + t1: 'Comment', + t2: 'RedditUser', + t3: 'Submission', + t4: 'PrivateMessage', + t5: 'Subreddit', + t6: 'Trophy', + t8: 'PromoCampaign', + Listing: 'Listing', + more: 'More', + UserList: 'UserList', + KarmaList: 'KarmaList', + TrophyList: 'TrophyList', + subreddit_settings: 'SubredditSettings', + modaction: 'ModAction', + wikipage: 'WikiPage', + wikipagesettings: 'WikiPageSettings', + wikipagelisting: 'WikiPageListing', + LiveUpdateEvent: 'LiveThread', + LiveUpdate: 'LiveUpdate', + LabeledMulti: 'MultiReddit', + ModmailConversation: 'ModmailConversation', + ModmailConversationAuthor: 'ModmailConversationAuthor' +} as const +export const USERNAME_REGEX = /^[\w-]{1,20}$/ +export const SUBMISSION_ID_REGEX = /comments\/(.+?)\// +export const PLACEHOLDER_REGEX = /{(\w+)}/g +export const MODERATOR_PERMISSIONS = ['wiki', 'posts', 'access', 'mail', 'config', 'flair'] +export const LIVETHREAD_PERMISSIONS = ['update', 'edit', 'manage'] +export const HTTP_VERBS = ['delete', 'get', 'head', 'patch', 'post', 'put'] as const +export const IDEMPOTENT_HTTP_VERBS = ['delete', 'get', 'head', 'put'] as const +export const MAX_TOKEN_LATENCY = 10000 +export const MAX_API_INFO_AMOUNT = 100 +export const MAX_API_MORECHILDREN_AMOUNT = 20 +export const MAX_LISTING_ITEMS = 100 +export const MIME_TYPES = { + png: 'image/png', + mov: 'video/quicktime', + mp4: 'video/mp4', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + gif: 'image/gif' +}; +export const MEDIA_TYPES = { + img: 'image', + video: 'video', + gif: 'video' +}; +export const GRANT_TYPES = { + CLIENT_CREDENTIALS: 'client_credentials', + INSTALLED_CLIENT: 'https://oauth.reddit.com/grants/installed_client', + REFRESH_TOKEN: 'refresh_token', + PASSWORD: 'password', + AUTHORIZATION_CODE: 'authorization_code' +} +export const DEVICE_ID = 'DO_NOT_TRACK_THIS_DEVICE' +export const SUBMISSION_SORTS = ['hot', 'new', 'top', 'rising', 'controversial'] as const +export const FRONTPAGE_SORTS = ['best', ...SUBMISSION_SORTS] as const +export const COMMENT_SORTS = ['confidence', 'top', 'new', 'controversial', 'old', 'random', 'qa', 'live'] as const diff --git a/src/create_config.js b/src/create_config.js deleted file mode 100644 index a7a24a7a..00000000 --- a/src/create_config.js +++ /dev/null @@ -1,39 +0,0 @@ -import {addSnakeCaseShadowProps} from './helpers.js'; - -export const consoleLogger = Object.freeze({ - warn (...args) { - // eslint-disable-next-line no-console - console.warn('[warning]', ...args); - }, - - info (...args) { - // eslint-disable-next-line no-console - console.info('[info]', ...args); - }, - - debug (...args) { - // eslint-disable-next-line no-console - console.debug('[debug]', ...args); - }, - - trace (...args) { - // eslint-disable-next-line no-console - console.trace('[trace]', ...args); - } -}); - -export default function () { - const config = Object.create(null); - config.endpointDomain = 'reddit.com'; - config.requestDelay = 0; - config.requestTimeout = 30000; - config.continueAfterRatelimitError = false; - config.retryErrorCodes = [502, 503, 504, 522]; - config.maxRetryAttempts = 3; - config.warnings = true; - config.debug = false; - config.logger = consoleLogger; - config.proxies = true; - - return addSnakeCaseShadowProps(config); -} diff --git a/src/defaultConfig.ts b/src/defaultConfig.ts new file mode 100644 index 00000000..f1fe9420 --- /dev/null +++ b/src/defaultConfig.ts @@ -0,0 +1,30 @@ +export const consoleLogger = Object.freeze({ + warn (...args: any[]) { + // eslint-disable-next-line no-console + console.warn('[warning]', ...args) + }, + info (...args: any[]) { + // eslint-disable-next-line no-console + console.info('[info]', ...args) + }, + debug (...args: any[]) { + // eslint-disable-next-line no-console + console.debug('[debug]', ...args) + }, + trace (...args: any[]) { + // eslint-disable-next-line no-console + console.trace('[trace]', ...args) + } +}) + +export default { + endpointDomain: 'reddit.com', + requestDelay: 0, + requestTimeout: 30000, + continueAfterRatelimitError: false, + retryErrorCodes: [502, 503, 504, 522], + maxRetryAttempts: 3, + warnings: true, + debug: false, + logger: consoleLogger +} diff --git a/src/defaultObjects.ts b/src/defaultObjects.ts new file mode 100644 index 00000000..ca4e56b4 --- /dev/null +++ b/src/defaultObjects.ts @@ -0,0 +1,23 @@ +import RedditContent from './objects/RedditContent' +import {KINDS} from './constants' + +const extendRedditContent = (name: string) => { + return { + [name]: class extends RedditContent { + static _name = name + } + }[name] +} + +const defaultObjects: { + [Key in typeof KINDS[keyof typeof KINDS]]: ReturnType +} = (() => { + const obj: any = {} + for (const key of Object.keys(KINDS)) { + const value = KINDS[key] + obj[value] = extendRedditContent(value) + } + return obj +})() + +export default defaultObjects diff --git a/src/errors.js b/src/errors.js deleted file mode 100644 index c267ba7c..00000000 --- a/src/errors.js +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable max-len */ -import {API_RULES_LINK, DOCS_LINK, MODULE_NAME} from './constants.js'; - -export class RateLimitError extends Error { - constructor () { - super(`${MODULE_NAME} refused to continue because reddit's ratelimit was exceeded. For more information about reddit's ratelimit, please consult reddit's API rules at ${API_RULES_LINK}.`); - } -} - -export class InvalidUserError extends Error { - constructor (message = 'Cannot fetch information on the given user. Please be sure you have the right username.') { - super(message); - } -} - -export class NoCredentialsError extends Error { - constructor (message = `Missing credentials passed to ${MODULE_NAME} constructor. You must pass an object containing either (a) userAgent, clientId, clientSecret, and refreshToken properties, (b) userAgent and accessToken properties, or (c) userAgent, clientId, clientSecret, username, and password properties. For information, please read the docs at ${DOCS_LINK}.`) { - super(message); - } -} - -export class InvalidMethodCallError extends Error {} -export class RequestError extends Error {} -export class StatusCodeError extends Error {} - -export function rateLimitWarning (millisecondsUntilReset) { - return `Warning: ${MODULE_NAME} temporarily stopped sending requests because reddit's ratelimit was exceeded. The request you attempted to send was queued, and will be sent to reddit when the current ratelimit period expires in ${millisecondsUntilReset / 1000} seconds.`; -} diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 00000000..a5a4bb71 --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,35 @@ +/* eslint-disable max-len */ +import {API_RULES_LINK, DOCS_LINK, MODULE_NAME} from './constants' + +export class RateLimitError extends Error { + constructor () { + super(`${MODULE_NAME} refused to continue because reddit's ratelimit was exceeded. For more information about reddit's ratelimit, please consult reddit's API rules at ${API_RULES_LINK}.`) + } +} + +export class InvalidUserError extends Error { + constructor (name: string) { + super(`Cannot fetch information on the given user: ${name}. Please be sure you have the right username.`) + } +} + +export class NoCredentialsError extends Error { + constructor () { + super(`Missing credentials passed to ${MODULE_NAME} constructor. You must pass an object containing either (a) userAgent, clientId, clientSecret, and refreshToken properties, (b) userAgent and accessToken properties, or (c) userAgent, clientId, clientSecret, username, and password properties. For information, please read the docs at ${DOCS_LINK}.`) + } +} + +export class MediaPostFailedError extends Error { + constructor () { + super('The attempted media upload action has failed. Possible causes include the corruption of media files.') + } +} + +export class InvalidMethodCallError extends Error {} +export class RequestError extends Error {} +export class StatusCodeError extends Error {} +export class WebSocketError extends Error {} + +export function rateLimitWarning (millisecondsUntilReset: number) { + return `Warning: ${MODULE_NAME} temporarily stopped sending requests because reddit's ratelimit was exceeded. The request you attempted to send was queued, and will be sent to reddit when the current ratelimit period expires in ${millisecondsUntilReset / 1000} seconds.` +} diff --git a/src/helpers.js b/src/helper.ts similarity index 82% rename from src/helpers.js rename to src/helper.ts index d20cb53a..488cacc0 100644 --- a/src/helpers.js +++ b/src/helper.ts @@ -1,7 +1,8 @@ +// @ts-nocheck import util from 'util'; -import {find, includes, isEmpty, keyBy, omit, partial, property, remove, snakeCase} from 'lodash'; -import {MODERATOR_PERMISSIONS, LIVETHREAD_PERMISSIONS} from './constants.js'; -import {emptyChildren as emptyMoreObject} from './objects/More.js'; +import {find, includes, isEmpty, omit, partial, property, snakeCase} from 'lodash'; +import {MODERATOR_PERMISSIONS, LIVETHREAD_PERMISSIONS} from './constants'; +import {emptyChildren as emptyMoreObject} from './objects/More'; /** * @summary Returns an unfetched empty replies Listing for an item. @@ -13,7 +14,7 @@ export function getEmptyRepliesListing (item) { if (item.constructor._name === 'Comment') { return item._r._newObject('Listing', { _uri: `comments/${(item.link_id || item.parent_id).slice(3)}`, - _query: {comment: item.name.slice(3)}, + _query: {comment: item.name.slice(3), sort: item._sort}, _transform: property('comments[0].replies'), _link_id: item.link_id, _isCommentList: true @@ -40,13 +41,10 @@ export function addEmptyRepliesListing (item) { return item; } -export function handleJsonErrors (returnValue) { - return response => { - if (isEmpty(response) || isEmpty(response.json.errors)) { - return returnValue; - } +export function handleJsonErrors (response) { + if (!isEmpty(response) && !isEmpty(response.json.errors)) { throw new Error(response.json.errors[0]); - }; + } } /** @@ -96,18 +94,25 @@ tree so that replies are threaded properly. * @api private */ export function buildRepliesTree (childList) { - const childMap = keyBy(childList, 'name'); - childList.forEach(addEmptyRepliesListing); - childList.filter(child => child.constructor._name === 'Comment').forEach(child => child.replies._more = emptyMoreObject); - remove(childList, child => childMap[child.parent_id]).forEach(child => { - if (child.constructor._name === 'More') { - childMap[child.parent_id].replies._setMore(child); - child.link_id = childMap[child.parent_id].link_id; - } else { - childMap[child.parent_id].replies.push(child); + const childMap = {} + for (const child of childList) { + childMap[child.name] = child + if (child.constructor._name !== 'More') addEmptyRepliesListing(child) + if (child.constructor._name === 'Comment') child.replies._more = emptyMoreObject() + } + const childItems = childList.filter(child => { + if (childMap[child.parent_id]) { + if (child.constructor._name === 'More') { + childMap[child.parent_id].replies._setMore(child) + child.link_id = childMap[child.parent_id].link_id + } else { + childMap[child.parent_id].replies.push(child) + } + return false } - }); - return childList; + return true + }) + return childItems } /** @@ -146,7 +151,9 @@ of the property. */ export function addSnakeCaseShadowProps (obj) { Object.keys(obj).filter(key => !key.startsWith('_') && key !== snakeCase(key)).forEach(key => { - Object.defineProperty(obj, snakeCase(key), {get: () => obj[key], set: value => obj[key] = value}); + Object.defineProperty(obj, snakeCase(key), {get: () => obj[key], set: value => { + obj[key] = value; + }}); }); return obj; } @@ -154,12 +161,10 @@ export function addSnakeCaseShadowProps (obj) { export const isBrowser = typeof self === 'object'; export function defineInspectFunc (obj, inspectFunc) { - if (isBrowser) { - return; - } + if (isBrowser) return // Use the util.inspect.custom symbol if available (Node 6.6.0+) - const inspectKey = util.inspect && typeof util.inspect.custom === 'symbol' ? util.inspect.custom : 'inspect'; - Object.defineProperty(obj, inspectKey, {writable: true, enumerable: false, value: inspectFunc}); + const inspectKey = util.inspect && typeof util.inspect.custom === 'symbol' ? util.inspect.custom : 'inspect' + Object.defineProperty(obj, inspectKey, {writable: true, enumerable: false, value: inspectFunc}) } export function requiredArg (argName) { diff --git a/src/helpers/FormData.ts b/src/helpers/FormData.ts new file mode 100644 index 00000000..8bad6755 --- /dev/null +++ b/src/helpers/FormData.ts @@ -0,0 +1,5 @@ +import FormDataNode from 'form-data' +import isBrowser from './isBrowser' + +const FormData: typeof FormDataNode = isBrowser ? self.FormData as any : FormDataNode +export default FormData diff --git a/src/helpers/URL.ts b/src/helpers/URL.ts new file mode 100644 index 00000000..0975155a --- /dev/null +++ b/src/helpers/URL.ts @@ -0,0 +1,5 @@ +import url from 'url' +import isBrowser from './isBrowser' + +const URL = isBrowser ? self.URL : url.URL +export default URL diff --git a/src/helpers/URLSearchParams.ts b/src/helpers/URLSearchParams.ts new file mode 100644 index 00000000..3871ea4c --- /dev/null +++ b/src/helpers/URLSearchParams.ts @@ -0,0 +1,5 @@ +import url from 'url' +import isBrowser from './isBrowser' + +const URLSearchParams = isBrowser ? self.URLSearchParams : url.URLSearchParams +export default URLSearchParams diff --git a/src/helpers/WebSocket.ts b/src/helpers/WebSocket.ts new file mode 100644 index 00000000..e64964e2 --- /dev/null +++ b/src/helpers/WebSocket.ts @@ -0,0 +1,5 @@ +import ws from 'ws' +import isBrowser from './isBrowser' + +const WebSocket: typeof self.WebSocket = isBrowser ? self.WebSocket : ws as any +export default WebSocket diff --git a/src/helpers/index.ts b/src/helpers/index.ts new file mode 100644 index 00000000..d7608dc0 --- /dev/null +++ b/src/helpers/index.ts @@ -0,0 +1,10 @@ +export {default as FormData} from './FormData' +export {default as isAxiosResponse} from './isAxiosResponse' +export {default as isBrowser} from './isBrowser' +export {default as isContentTree} from './isContentTree' +export {default as isSubmissionTree} from './isSubmissionTree' +export {default as path} from './path' +export {default as requiredArg} from './requiredArg' +export {default as URL} from './URL' +export {default as URLSearchParams} from './URLSearchParams' +export {default as WebSocket} from './WebSocket' diff --git a/src/helpers/isAxiosResponse.ts b/src/helpers/isAxiosResponse.ts new file mode 100644 index 00000000..1296e6dc --- /dev/null +++ b/src/helpers/isAxiosResponse.ts @@ -0,0 +1,17 @@ +const isAxiosResponse = (obj: any) => { + if ( + obj && + typeof obj === 'object' && + 'data' in obj && + 'status' in obj && + 'statusText' in obj && + 'headers' in obj && + 'config' in obj + ) { + return true + } + return false +} + +export default isAxiosResponse +export type {AxiosResponse} from '../axiosCreate' diff --git a/src/helpers/isBrowser.ts b/src/helpers/isBrowser.ts new file mode 100644 index 00000000..b57e9e8a --- /dev/null +++ b/src/helpers/isBrowser.ts @@ -0,0 +1,2 @@ +const isBrowser = typeof self === 'object' +export default isBrowser diff --git a/src/helpers/isContentTree.ts b/src/helpers/isContentTree.ts new file mode 100644 index 00000000..f0a788a2 --- /dev/null +++ b/src/helpers/isContentTree.ts @@ -0,0 +1,20 @@ +import {KINDS} from '../constants' + +interface ContentTree { + kind: keyof typeof KINDS + data: any +} + +const isContentTree = (obj: any) => { + if ( + Object.keys(obj).length === 2 && + obj.kind && + obj.data + ) { + return true + } + return false +} + +export default isContentTree +export {ContentTree} diff --git a/src/helpers/isSubmissionTree.ts b/src/helpers/isSubmissionTree.ts new file mode 100644 index 00000000..b660bf0b --- /dev/null +++ b/src/helpers/isSubmissionTree.ts @@ -0,0 +1,24 @@ +import Listing from '../objects/Listing' +import Submission from '../objects/Submission' +import Comment from '../objects/Comment' + +interface SubmissionTree { + 0: Listing, + 1: Listing +} + +const isSubmissionTree = (obj: any) => { + if ( + Array.isArray(obj) && + obj.length === 2 && + obj[0] instanceof Listing && + obj[0][0] instanceof Submission && + obj[1] instanceof Listing + ) { + return true + } + return false +} + +export default isSubmissionTree +export {SubmissionTree} diff --git a/src/helpers/path.ts b/src/helpers/path.ts new file mode 100644 index 00000000..edf08448 --- /dev/null +++ b/src/helpers/path.ts @@ -0,0 +1,6 @@ +import nodePath from 'path' +import browserPath from 'path-browserify' +import isBrowser from './isBrowser' + +const path = isBrowser ? browserPath : nodePath +export default path diff --git a/src/helpers/requiredArg.ts b/src/helpers/requiredArg.ts new file mode 100644 index 00000000..e985d600 --- /dev/null +++ b/src/helpers/requiredArg.ts @@ -0,0 +1,3 @@ +export default function requiredArg (argName: string): any { + throw new TypeError(`Missing required argument \`${argName}\``) +} diff --git a/src/interfaces.ts b/src/interfaces.ts new file mode 100644 index 00000000..e805d9e9 --- /dev/null +++ b/src/interfaces.ts @@ -0,0 +1,484 @@ +import type stream from 'stream' +import type snoowrap from './snoowrap' +import type {Comment} from './objects' +import type {MediaImg, MediaVideo, MediaGif} from './objects/MediaFile' +import type {COMMENT_SORTS} from './constants' + +export type OmitProps = { + [P in keyof T as Exclude]: T[P] +} + +export interface CredentialsResponse { + access_token: string + expires_in: number + refresh_token: string + scope: string + token_type: string + error: string + error_description: string +} + +export interface JSONResponse { + json: { + data?: { + things: T[] + drafts_count: number + id: string + name: string + url: string + [key: string]: any + } + error: string[][] + } + /** Custom */ + _children: {[id: string]: Comment} +} + +export interface Fancypants { + assets: { + caption: string|null + id: string + obfuscation_descriptor: any[] + obfuscation_reason: string|null + }[] + output: { + document: { + c?: string|Fancypants['output']['document'] + e?: string + f?: number[][] + id?: string + t?: string + }[] + } + /** @example 'rtjson' */ + output_mode: string +} + +export interface UploadResponse { + args: { + /** @example '//reddit-uploaded-video.s3-accelerate.amazonaws.com' */ + action: string + /** Headers */ + fields: { + name: string + value: string + }[] + } + asset: { + asset_id: string + payload: { + filepath: string + } + processing_state: string + /** @example 'wss://ws-0ffbcc476ebd6c972.wss.redditmedia.com/?m=' */ + websocket_url: string + } +} + +export interface Children { + [key: string]: Comment +} + +// #region Submit + +export interface UploadMediaOptions { + /** + * The media file to upload. This should either be the path to the file (as a `string`), + * or a [stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable), + * or a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob), + * or a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments where + * the filesystem is unavailable (e.g. browsers). + */ + file: string|stream.Readable|Blob|File + /** + * The name that the file should have. Required when it cannot get diractly extracted from the provided + * file (e.g ReadableStream, Blob). + */ + name?: string + /** Determines the media file type. This should be one of `img, video, gif`. */ + type?: 'img'|'video'|'gif' + /** + * A caption for the embedded file to be used on selfposts bodies and gallery items. + * @desc **NOTE**: Captions on gallery items must be 180 characters or less. + */ + caption?: string + /** An external URL to be used on gallery items. */ + outboundUrl?: string + /** + * If `true`, the file won't get uploaded, and this method will return `null`. Useful if you only want + * to validate the parameters before actually uploading the file. + */ + validateOnly?: boolean +} + +export interface UploadInlineMediaOptions extends UploadMediaOptions { + type: 'img'|'video'|'gif' +} + +export interface MediaType { + readonly img: MediaImg + readonly video: MediaVideo + readonly gif: MediaGif +} + +export interface SubmitOptions { + /** The name of the subreddit where to submit the post. */ + sr: string + /** The submission kind. */ + kind: 'link'|'image'|'video'|'videogif'|'gallery'|'self'|'poll'|'crosspost' + /** The title of the submission. */ + title: string + /** The url that the link submission should point to. */ + url: string + /** The video thumbnail url. */ + video_poster_url: string + /** The url of the websocket used to obtain the id of the newly-created submission. */ + websocketUrl: string + /** An array containing raw gallery items.*/ + items: { + media_id: string + caption?: string + outbound_url?: string + }[] + /** The selftext of the submission. */ + text?: string + /** + * The body of the submission in `richtext_json` format. See {@link snoowrap#convertToFancypants} + * for more details. This will override `options.text` and `options.inlineMedia`. + */ + richtext_json?: {[key: string]: any} + /** An array of 2 to 6 poll options. */ + options: string[] + /** The number of days the poll should accept votes. Valid values are between 1 and 7, inclusive. */ + duration: number + /** The fullname of the original post which is being crossposted. */ + crosspost_fullname: string + /** + * If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + */ + resubmit?: boolean + /** Determines whether inbox replies should be enabled for this submission. */ + sendreplies?: boolean + /** Whether or not the submission should be marked NSFW. */ + nsfw?: boolean + /** Whether or not the submission should be marked as a spoiler. */ + spoiler?: boolean + /** The flair template to select. */ + flair_id?: string + /** + * If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + */ + flair_text?: string + /** The UUID of a collection to add the newly-submitted post to. */ + collection_id?: string + /** Set to `CHAT` to enable live discussion instead of traditional comments. */ + discussion_type?: string + /** + * A captcha identifier. This is only necessary if the authenticated account + * requires a captcha to submit posts and comments. + */ + iden?: string + /** The response to the captcha with the given identifier. */ + captcha?: string + [key: string]: any +} + +export interface SubmitLinkOptions extends OmitProps< + SubmitOptions, + 'kind'|'video_poster_url'|'websocketUrl'|'items'|'text'|'richtext_json'|'options'|'duration'|'crosspost_fullname' +> {} + +export interface SubmitImageOptions extends OmitProps< + SubmitOptions, + 'kind'|'url'|'video_poster_url'|'websocketUrl'|'items'|'text'|'richtext_json'|'options'|'duration'|'crosspost_fullname' +> { + /** + * The image to submit. This should either be the path to the image file you want to upload, + * or a [stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable), + * or a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob), + * or a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments where + * the filesystem is unavailable (e.g. browsers). Alternatively you can just pass a ready-to-use + * {@link MediaImg} instead. See {@link snoowrap#uploadMedia} for more details. + */ + imageFile: MediaImg|UploadMediaOptions['file'] + /** + * The name that the image file should have. Required when it cannot get diractly extracted from + * the provided file (e.g ReadableStream, Blob). + */ + imageFileName?: string + /** + * Set to `true` to disable the use of WebSockets. If `true`, this method will return `null`. + */ + noWebsockets: boolean +} + +export interface SubmitVideoOptions extends OmitProps< + SubmitOptions, + 'kind'|'url'|'video_poster_url'|'websocketUrl'|'items'|'text'|'richtext_json'|'options'|'duration'|'crosspost_fullname' +> { + /** + * The video to submit. This should either be the path to the video file you want to upload, + * or a [stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable), + * or a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob), + * or a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments where + * the filesystem is unavailable (e.g. browsers). Alternatively you can just pass a ready-to-use + * {@link MediaVideo} instead. See {@link snoowrap#uploadMedia} for more details. + */ + videoFile: string|stream.Readable|Blob|File|MediaVideo + /** + * The name that the video file should have. Required when it cannot get diractly extracted from + * the provided file (e.g ReadableStream, Blob). + */ + videoFileName?: string + /** + * The thumbnail image to use. This should either be the path to the image file you want to upload, + * or a [stream.Readable](https://nodejs.org/api/stream.html#stream_class_stream_readable), + * or a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob), + * or a [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments where + * the filesystem is unavailable (e.g. browsers). Alternatively you can just pass a ready-to-use + * {@link MediaImg} instead. See {@link snoowrap#uploadMedia} for more details. + */ + thumbnailFile: string|stream.Readable|Blob|File|MediaImg + /** + * The name that the thumbnail file should have. Required when it cannot be diractly extracted from + * the provided file (e.g ReadableStream, Blob). + */ + thumbnailFileName?: string + /** + * If `true`, the video is submitted as a `videogif`, which is essentially a silent video. + */ + videogif: boolean + /** + * Set to `true` to disable the use of WebSockets. If `true`, this method will return `null`. + */ + noWebsockets: boolean +} + +export interface SubmitGalleryOptions extends OmitProps< + SubmitOptions, + 'kind'|'url'|'video_poster_url'|'websocketUrl'|'text'|'richtext_json'|'options'|'duration'|'crosspost_fullname' +> { + /** + * An array containing 2 to 20 gallery items. Currently only images are accepted. A gallery item should + * either be a {@link MediaImg}, or an {@link UploadMediaOptions} object to be passed to {@link snoowrap#uploadMedia} + * (`UploadMediaOptions.type` will be enforced to be `img`). + */ + gallery: Array +} + +export interface SubmitSelfpostOptions extends OmitProps< + SubmitOptions, + 'kind'|'url'|'video_poster_url'|'websocketUrl'|'items'|'options'|'duration'|'crosspost_fullname' +> { + /** + * An object containing inctances of {@link MediaFile} subclasses, or {@link UploadInlineMediaOptions} objects + * to be passed to {@link snoowrap#uploadMedia} where `options.type` is required. + * The keys of this object can be used as placeholders to embed media in `options.text` with the format `{key}`. + */ + inlineMedia: {[key: string]: MediaImg|MediaVideo|MediaGif|UploadInlineMediaOptions} +} + +export interface SubmitPollOptions extends OmitProps< + SubmitOptions, + 'kind'|'url'|'video_poster_url'|'websocketUrl'|'items'|'text'|'richtext_json'|'crosspost_fullname' +> {} + +export interface SubmitCrosspostOptions extends OmitProps< + SubmitOptions, + 'kind'|'url'|'video_poster_url'|'websocketUrl'|'items'|'text'|'richtext_json'|'options'|'duration' +> { + /* A Submission object or a post ID for the original post which is being crossposted **/ + originalPost: InstanceType|string +} + +// #endregion + +export interface SubredditOptions { + name: string + title: string + public_description: string + default_set: boolean + description: string + allow_images: boolean + allow_top: boolean + captcha: string + captcha_iden: string + collapse_deleted_comments: boolean + comment_score_hide_mins: number + exclude_banned_modqueue: boolean + 'header-title': string + hide_ads: boolean + lang: string + link_type: 'any'|'link'|'self' + over_18: boolean + public_traffic: boolean + show_media: boolean + show_media_preview: boolean + spam_comments: 'low'|'high'|'all' + spam_links: 'low'|'high'|'all' + spam_selfposts: 'low'|'high'|'all' + spoilers_enabled: boolean + sr: string + submit_link_label: string + submit_text_label: string + submit_text: string + suggested_comment_sort: typeof COMMENT_SORTS[number] + type: 'public'|'private'|'restricted'|'gold_restricted'|'gold_only'|'archived'|'employees_only' + wiki_edit_age: number + wiki_edit_karma: number + wikimode: string + [key: string]: any +} + +export interface Gildings { + /** Number of Reddit Silver awarded */ + gid_1: number + /** Number of Reddit Gold awarded */ + gid_2: number + /** Number of Reddit Platinum awarded */ + gid_3: number +} + +export type SubredditType = 'public'|'private'|'restricted'|'gold_restricted'|'gold_only'|'archived'|'employees_only'|'user' + +// #region Submission + +export interface RichTextFlair { + /** The string representation of the emoji */ + a?: string + /** The type of the flair entry */ + e: 'text'|'emoji' + /** URL of the emoji image */ + u?: string + /** The text content of a text flair */ + t?: string +} + +export interface Media { + oembed?: { + /** The username of the uploader of the source media */ + author_name?: string + /** URL to the author's profile on the source website */ + author_url?: string + description?: string + height: number + html: string + /** Name of the source website, e.g. "gfycat", "YouTube" */ + provider_name: string + /** URL of the source website, e.g. "https://www.youtube.com" */ + provider_url: string + thumbnail_height: number + thumbnail_url: string + thumbnail_width: number + /** Name of the media on the content site, e.g. YouTube video title */ + title: string + type: 'video'|'rich' + version: string + width: number + } + reddit_video?: { + dash_url: string + duration: number + fallback_url: string + height: number + hls_url: string + is_gif: boolean + scrubber_media_url: string + transcoding_status: string + } + type?: string +} + +export interface MediaEmbed { + /** HTML string of the media, usually an iframe */ + content?: string + height?: number + scrolling?: boolean + width?: number +} + +export interface SecureMediaEmbed extends MediaEmbed { + media_domain_url?: string +} + +export interface ImagePreviewSource { + url: string + width: number + height: number +} + +export interface ImagePreview { + source: ImagePreviewSource + resolutions: ImagePreviewSource[] + variants: any // ? + id: string +} + +// #endregion + + +export interface AssignFlairOptions { + /** The text that the flair should have */ + text?: string + /** The CSS class that the user's flair should have */ + css_class?: string + /** The name of the user that flair should be assigned to */ + name?: string + /** The name of the submission that flair should be assigned to */ + link?: string + /** The subreddit that flair should be assigned on */ + subredditName: string + [key: string]: any +} + +export interface SelectFlairOptions { + /** + * The text that the flair should have. (This is only necessary/useful if the given flair + * template has the `text_editable` property set to `true`.) + */ + text?: string + /** + * A flair template ID to use. (This should be obtained beforehand using + * {@link Subreddit#getUserFlairTemplates}.) + */ + flair_template_id: string + /** The name of the user that flair should be assigned to */ + name?: string + /** The name of the submission that flair should be assigned to */ + link?: string + /** The subreddit that flair should be assigned on */ + subredditName: string + [key: string]: any +} + +export interface CreateFlairOptions { + /** The text that the flair should have */ + text: string + /** The CSS class that the user's flair should have */ + css_class?: string + /** The type that the flair should have */ + flair_type: 'USER_FLAIR'|'LINK_FLAIR' + /** + * Determines whether users should be able to edit their flair text + * when it has this template + */ + text_editable?: boolean + [key: string]: any +} + +export interface FlairSelectorOptions { + name?: string + link?: string + is_newlink?: boolean +} + +export type FlairCSVOptions = { + /** The text that the flair should have */ + text?: string + /** The CSS class that the user's flair should have */ + css_class?: string + /** The name of the user that flair should be assigned to */ + name: string +}[] diff --git a/src/objects/Comment.d.ts b/src/objects/Comment.d.ts deleted file mode 100644 index 774fbb6d..00000000 --- a/src/objects/Comment.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Listing from './Listing'; -import RedditUser from './RedditUser'; -import Subreddit from './Subreddit'; -import VoteableContent from './VoteableContent'; - -export default class Comment extends VoteableContent { - approved: boolean; - body_html: string; - body: string; - collapsed_reason: any; // ? - collapsed: boolean; - controversiality: number; - depth: number; - ignore_reports: boolean; - /** True if comment author is the same as the Submission author */ - is_submitter: boolean; - link_id: string; - parent_id: string; - removed: boolean; - replies: Listing; - score_hidden: boolean; - spam: boolean; -} diff --git a/src/objects/Comment.js b/src/objects/Comment.js deleted file mode 100644 index db8203fa..00000000 --- a/src/objects/Comment.js +++ /dev/null @@ -1,57 +0,0 @@ -import {addEmptyRepliesListing, getEmptyRepliesListing} from '../helpers.js'; -import Listing from './Listing.js'; -import {emptyChildren as emptyMoreObject} from './More.js'; -import VoteableContent from './VoteableContent.js'; -/** -* A class representing a reddit comment -* -* @example -* -* // Get a comment with the given ID -* r.getComment('c0hkuyq') -* -* @extends VoteableContent -*/ -const Comment = class Comment extends VoteableContent { - constructor (options, _r, _hasFetched) { - super(options, _r, _hasFetched); - if (_hasFetched) { - /* If a comment is in a deep comment chain, reddit will send a single `more` object with name `t1__` in place of the - comment's replies. This is the equivalent of seeing a 'Continue this thread' link on the HTML site, and it indicates that - replies should be fetched by sending another request to view the deep comment alone, and parsing the replies from that. */ - if (this.replies instanceof Listing && !this.replies.length && this.replies._more && this.replies._more.name === 't1__') { - this.replies = getEmptyRepliesListing(this); - } else if (this.replies === '') { - /* If a comment has no replies, reddit returns an empty string as its `replies` property rather than an empty Listing. - This behavior is unexpected, so replace the empty string with an empty Listing. */ - this.replies = this._r._newObject('Listing', {children: [], _more: emptyMoreObject, _isCommentList: true}); - } else if (this.replies._more && !this.replies._more.link_id) { - this.replies._more.link_id = this.link_id; - } - } - } - _transformApiResponse (response) { - return addEmptyRepliesListing(response[0]); - } - get _uri () { - return `api/info?id=${this.name}`; - } - /** - * @summary Locks this Comment, preventing new comments from being posted on it. - * @returns {Promise} The updated version of this Comment - * @example r.getComment('d1xclfo').lock() - */ - lock () { - return this._post({uri: 'api/lock', form: {id: this.name}}).return(this); - } - /** - * @summary Unlocks this Comment, allowing comments to be posted on it again. - * @returns {Promise} The updated version of this Comment - * @example r.getComment('d1xclfo').unlock() - */ - unlock () { - return this._post({uri: 'api/unlock', form: {id: this.name}}).return(this); - } -}; - -export default Comment; diff --git a/src/objects/Comment.ts b/src/objects/Comment.ts new file mode 100644 index 00000000..da3c061c --- /dev/null +++ b/src/objects/Comment.ts @@ -0,0 +1,151 @@ +import {addEmptyRepliesListing, getEmptyRepliesListing} from '../helper' +import {emptyChildren as emptyMoreObject} from './More' +import VoteableContent from './VoteableContent' +import type snoowrap from '../snoowrap' +import type {Listing, Submission} from './' +import type {FetchMoreOptions, FetchAllOptions} from './Listing' +import type {OmitProps} from '../interfaces' +import type {COMMENT_SORTS} from '../constants' + + +interface Comment { + body_html: string + body: string + collapsed_reason: any // ? + collapsed: boolean + controversiality: number + created: number + created_utc: number + depth: number + id: string + ignore_reports: boolean + /** True if comment author is the same as the Submission author */ + is_submitter: boolean + link_id: string + name: string + parent_id: string + removed: boolean + replies: Listing + score_hidden: boolean + spam: boolean +} + +/** + * A class representing a reddit comment + * @example + * + * // Get a comment with the given ID + * r.getComment('c0hkuyq') + */ +class Comment extends VoteableContent { + static _name = 'Comment' + + _sort?: typeof COMMENT_SORTS[number] + _children: {[id: string]: Comment} + _cb?: (child: {_children: {[id: string]: Comment}}) => void + + constructor ( + {_children = {}, ...options}: {[key: string]: any}, + _r: snoowrap, + _hasFetched = false + ) { + super(options, _r, _hasFetched) + this._children = _children + + if (_hasFetched) { + if (options.replies === '') { + /** + * If a comment has no replies, reddit returns an empty string as its `replies` property rather than an empty Listing. + * This behavior is unexpected, so replace the empty string with an empty Listing. + */ + this.replies = this._r._newObject('Listing', {children: [], _more: emptyMoreObject(), _isCommentList: true}) + } else if (this.replies.constructor._name === 'Listing' && !this.replies.length && this.replies._more && this.replies._more.name === 't1__') { + /** + * If a comment is in a deep comment chain, reddit will send a single `more` object with name `t1__` in place of the + * comment's replies. This is the equivalent of seeing a 'Continue this thread' link on the HTML site, and it indicates that + * replies should be fetched by sending another request to view the deep comment alone, and parsing the replies from that. + */ + this.replies = getEmptyRepliesListing(this) + } else if (this.replies._more && !this.replies._more.link_id) { + this.replies._more.link_id = this.link_id + } + } + } + + _transformApiResponse (response: any) { + // Response of /comments + if (response.constructor._name === 'Submission') { + const submission: Submission = response + const comment: Comment = submission.comments[0] + const children = submission._children + delete children[comment.id] + comment._children = children + comment._sort = this._sort + comment._cb = this._cb + if (this._cb) this._cb(comment) + return comment + } + // Response of /api/info (empty _children, no parent submission) + const listing: Listing = response + const comment = listing[0] + comment._sort = this._sort + return addEmptyRepliesListing(comment) + } + + get _uri () { + return !this.link_id + ? `api/info?id=${this.name}` + : `comments/${this.link_id.slice(3)}?comment=${this.name.slice(3)}${this._sort ? `&sort=${this._sort}` : ''}` + } + + /** + * @summary Fetch more replies and append them automatically to the replies listing. All replies and their + * children will be exposed automatically to {@link Submission#getComment}. + * @param options - Object of fetching options or the number of replies to fetch. see + * {@link Listing#fetchMore} for more details. + * @returns A Promise that fulfills with the replies listing. + */ + async fetchMore (options: Partial>|number) { + if (typeof options !== 'number') { + options.append = true + } + const comments = await this.replies.fetchMore(options) + if (this._cb) { + this._cb({_children: comments._children}) + } + this.replies = comments + return comments + } + + /** + * @summary Fetch all replies and append them automatically to the replies listing. All replies and their + * children will be exposed automatically to {@link Submission#getComment}. + * @param {object} [options] - Fetching options. see {@link Listing#fetchAll} for more details. + * @returns A Promise that fulfills with the replies listing. + */ + fetchAll (options?: OmitProps) { + return this.fetchMore({...options, amount: Infinity}) + } + + /** + * @summary Locks this Comment, preventing new comments from being posted on it. + * @returns The updated version of this Comment + * @example r.getComment('d1xclfo').lock() + */ + async lock () { + await this._post({url: 'api/lock', form: {id: this.name}}) + return this + } + + /** + * @summary Unlocks this Comment, allowing comments to be posted on it again. + * @returns The updated version of this Comment + * @example r.getComment('d1xclfo').unlock() + */ + async unlock () { + await this._post({url: 'api/unlock', form: {id: this.name}}) + return this + } +} + +export default Comment diff --git a/src/objects/Listing.d.ts b/src/objects/Listing.d.ts deleted file mode 100644 index caebb3d7..00000000 --- a/src/objects/Listing.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as Snoowrap from '../snoowrap'; - -export default class Listing extends Array { - constructor(options: any, _r: Snoowrap); - isFinished: boolean; - is_finished: boolean; - fetchMore(options: FetchMoreOptions): Listing; - fetchAll(options?: FetchMoreOptions): Listing; - /* @deprecated */ fetchUntil(options?: FetchMoreOptions): Listing; - toJSON(): T[]; -} - -export interface ListingOptions { - limit?: number; - after?: string; - before?: string; - show?: string; - count?: number; -} - -export interface SortedListingOptions extends ListingOptions { - time?: 'all' | 'hour' | 'day' | 'week' | 'month' | 'year'; -} - -interface FetchMoreOptions { - amount: number; - skipReplies?: boolean; - skip_replies?: boolean; - append?: boolean; -} diff --git a/src/objects/Listing.js b/src/objects/Listing.js deleted file mode 100644 index 55d590f5..00000000 --- a/src/objects/Listing.js +++ /dev/null @@ -1,245 +0,0 @@ -import {clone, defaults, defaultsDeep, isEmpty, omitBy, pick} from 'lodash'; -import Promise from '../Promise.js'; -import util from 'util'; -import {parse as urlParse} from 'url'; -import {defineInspectFunc} from '../helpers.js'; -import {InvalidMethodCallError} from '../errors.js'; -import {default as More, emptyChildren} from './More.js'; - -const INTERNAL_DEFAULTS = { - _query: {}, - _transform: value => value, - _method: 'get', - _isCommentList: false, - _link_id: null, - _uri: null, - _more: null, - _cachedLookahead: null -}; - -/** -* A class representing a list of content. This is a subclass of the native Array object, so it has all the properties of -an Array (length, forEach, etc.) in addition to some added methods. The Listing can be extended by using the -[#fetchMore()]{@link Listing#fetchMore} and -[#fetchAll()]{@link Listing#fetchAll} functions. Note that these methods return new Listings, rather than mutating the -original Listing. -* -* Most methods that return Listings will also accept `limit`, `after`, `before`, `show`, and `count` properties. -* -* If you've used the reddit API before (or used other API wrappers like [PRAW](https://praw.readthedocs.org/en/stable/)), you -might know that reddit uses a `MoreComments` object in its raw JSON responses, representing comments that have been stubbed -out of Listings. In snoowrap, there are no exposed `MoreComments` objects; the objects returned by the reddit API are -stripped from Listings and are used internally as sources for the `fetchMore` functions. This means that in snoowrap, Listings -that contain Comments can be used/expanded in the same manner as Listings that don't contain Comments, and for the most part -you don't have to worry about the distinction. - -(Incidentally, if you encounter a Listing that *does* contain a `MoreComments` object then it's a bug, so please report it.) - -* -* @extends Array -*/ -const Listing = class Listing extends Array { - constructor (options = {}, _r) { - super(); - if (!(this instanceof Listing)) { - // Safari 9 has an incorrect implementation of classes that extend Arrays. As a workaround, - // manually set the constructor and prototype. - this.constructor = Listing; - Object.setPrototypeOf(this, Listing.prototype); - } - this.push(...options.children || []); - this._r = _r; - this._cachedLookahead = options._cachedLookahead; - defaultsDeep(this, pick(options, Object.keys(INTERNAL_DEFAULTS)), INTERNAL_DEFAULTS); - Object.assign(this._query, pick(options, ['before', 'after'])); - if (options.children && options.children[options.children.length - 1] instanceof More) { - this._setMore(this.pop()); - } - } - _setUri (value) { - const parsedUri = urlParse(value, true); - this._uri = parsedUri.pathname; - defaultsDeep(this._query, parsedUri.query); - if (parsedUri.query.before) { - this._query.after = null; - } else { - this._query.before = null; - } - } - /** - * @summary A getter that indicates whether this Listing has any more items to fetch. - * @type {boolean} - */ - get isFinished () { - // The process of checking whether a Listing is 'finished' varies depending on what kind of Listing it is. - return this._isCommentList - /* For comment Listings (i.e. Listings containing comments and comment replies, sourced by `more` objects): A Listing is - *never* finished if it has a cached lookahead (i.e. extra items that were fetched from a previous request). If there is - no cached lookahead, a Listing is finished iff it has an empty `more` object. */ - ? isEmpty(this._cachedLookahead) && !!this._more && isEmpty(this._more.children) - /* For non-comment Listings: A Listing is always finished if it has no URI (since there would be nowhere to fetch items - from). If it has a URI, a Listing is finished iff its `before` and `after` query are both `null`. This is because reddit - returns a value of `null` as the `after` and `before` parameters to signify that a Listing is complete. - - It is important to check for `null` here rather than any falsey value, because when an empty Listing is initialized, its - `after` and `before` properties are both `undefined`, but calling these empty Listings `finished` would be incorrect. */ - : !this._uri || (this._query.after === null && this._query.before === null); - } - get is_finished () { - // camel-case alias for backwards-compatibility. - // As a getter, the `isFinished` property doesn't have an alias like everything else. - return this.isFinished; - } - /** - * @summary Fetches some more items - * @param {object} options - * @param {number} options.amount The number of items to fetch. - * @param {boolean} [options.skipReplies=false] For a Listing that contains comment objects on a Submission, this option can - be used to save a few API calls, provided that only top-level comments are being examined. If this is set to `true`, snoowrap - is able to fetch 100 Comments per API call rather than 20, but all returned Comments will have no fetched replies by default. - * - * Internal details: When `skipReplies` is set to `true`, snoowrap uses reddit's `api/info` endpoint to fetch Comments. When - `skipReplies` is set to `false`, snoowrap uses reddit's `api/morechildren` endpoint. It's worth noting that reddit does - not allow concurrent requests to the `api/morechildren` endpoint by the same account. - * @param {boolean} [options.append=true] If `true`, the resulting Listing will contain the existing elements in addition to - the newly-fetched elements. If `false`, the resulting Listing will only contain the newly-fetched elements. - * @returns {Promise} A new Listing containing the newly-fetched elements. If `options.append` is `true`, the new Listing will - also contain all elements that were in the original Listing. Under most circumstances, the newly-fetched elements will appear - at the end of the new Listing. However, if reverse pagination is enabled (i.e. if this Listing was created with a `before` - query parameter), then the newly-fetched elements will appear at the beginning. In any case, continuity is maintained, i.e. - the order of items in the Listing will be the same as the order in which they appear on reddit. - * @example - * r.getHot({limit: 25}).then(myListing => { - * console.log(myListing.length); // => 25 - * myListing.fetchMore({amount: 10}).then(extendedListing => { - * console.log(extendedListing.length); // => 35 - * }) - * }); - */ - fetchMore (options) { - const parsedOptions = defaults( - typeof options === 'number' ? {amount: options} : clone(options), - // Accept either `skip_replies` or `skipReplies` for backwards compatibility. - {append: true, skipReplies: options.skip_replies} - ); - if (typeof parsedOptions.amount !== 'number' || Number.isNaN(parsedOptions.amount)) { - throw new InvalidMethodCallError('Failed to fetch Listing. (`amount` parameter was missing or invalid)'); - } - if (parsedOptions.amount <= 0 || this.isFinished) { - return this._r._promiseWrap(Promise.resolve(parsedOptions.append ? this._clone() : this._clone()._empty())); - } - if (this._cachedLookahead) { - const cloned = this._clone(); - cloned.push(...cloned._cachedLookahead.splice(0, parsedOptions.amount)); - return cloned.fetchMore(parsedOptions.amount - cloned.length + this.length); - } - return this._r._promiseWrap( - this._more ? this._fetchMoreComments(parsedOptions) : this._fetchMoreRegular(parsedOptions) - ); - } - _fetchMoreRegular (options) { - const query = omitBy(clone(this._query), value => value === null || value === undefined); - if (!this._isCommentList) { - /* Reddit returns a different number of items per request depending on the `limit` querystring property specified in the - request. If no `limit` property is specified, reddit returns some number of items depending on the user's preferences - (currently 25 items with default preferences). If a `limit` property is specified, then reddit returns `limit` items per - batch. However, this is capped at 100, so if a `limit` larger than 100 items is specified, reddit will only return 100 - items in the batch. (The cap of 100 could plausibly change to a different amount in the future.) - - However, one caveat is that reddit's parser doesn't understand the javascript `Infinity` global. If `limit=Infinity` is - provided in the querystring, reddit won't understand the parameter so it'll just act as if no parameter was provided, and - will return 25 items in the batch. This is suboptimal behavior as far as snoowrap is concerned, because it means that 4 - times as many requests are needed to fetch the entire listing. - - To get around the issue, snoowrap caps the `limit` property at Number.MAX_SAFE_INTEGER when sending requests. This ensures - that `Infinity` will never be sent as part of the querystring, so reddit will always return the maximal 100 items per - request if the desired amount of items is large. */ - query.limit = Math.min(options.amount, Number.MAX_SAFE_INTEGER); - } - return this._r.oauthRequest({ - uri: this._uri, - qs: query, - method: this._method - }).then(this._transform).then(response => { - const cloned = this._clone(); - if (!options.append) { - cloned._empty(); - } - if (cloned._query.before) { - cloned.unshift(...response); - cloned._query.before = response._query.before; - cloned._query.after = null; - } else { - cloned.push(...response); - cloned._query.before = null; - cloned._query.after = response._query.after; - } - if (this._isCommentList) { - cloned._more = cloned._more || response._more || emptyChildren; - if (response.length > options.amount) { - cloned._cachedLookahead = Array.from(cloned.splice(options.amount)); - } - } - return cloned.fetchMore({...options, append: true, amount: options.amount - response.length}); - }); - } - /* Pagination for comments works differently than it does for most other things; rather than sending a link to the next page - within a Listing, reddit sends the last comment in the list as as a `more` object, with links to all the remaining comments - in the thread. */ - _fetchMoreComments (options) { - return this._more.fetchMore(options).then(moreComments => { - const cloned = this._clone(); - if (!options.append) { - cloned._empty(); - } - cloned.push(...moreComments); - cloned._more.children = cloned._more.children.slice(options.amount); - return cloned; - }); - } - /** - * @summary Fetches all of the items in this Listing, only stopping when there are none left. - * @param {object} [options] Fetching options -- see {@link Listing#fetchMore} - * @returns {Promise} A new fully-fetched Listing. Keep in mind that this method has the potential to exhaust your - ratelimit quickly if the Listing doesn't have a clear end (e.g. with posts on the front page), so use it with discretion. - * @example - * - * r.getMe().getUpvotedContent().fetchAll().then(console.log) - * // => Listing [ Submission { ... }, Submission { ... }, ... ] - */ - fetchAll (options) { - return this.fetchMore({...options, amount: Infinity}); - } - fetchUntil (options) { - this._r._warn('Listing#fetchUntil is deprecated -- use Listing#fetchMore instead.'); - return this.fetchMore({...options, append: true, amount: options.length - this.length}); - } - _clone ({deep = false} = {}) { - const properties = pick(this, Object.keys(INTERNAL_DEFAULTS)); - properties._query = clone(properties._query); - properties._cachedLookahead = clone(properties._cachedLookahead); - properties._more = this._more && this._more._clone(); - const shallowChildren = Array.from(this); - properties.children = deep - ? shallowChildren.map(item => '_clone' in item && typeof item._clone === 'function' ? item._clone({deep}) : item) - : shallowChildren; - return new Listing(properties, this._r); - } - _setMore (moreObj) { - this._more = moreObj; - this._isCommentList = true; - } - _empty () { - this.splice(0, this.length); - return this; - } - toJSON () { - return Array.from(this).map(item => item && item.toJSON ? item.toJSON() : item); - } -}; - -defineInspectFunc(Listing.prototype, function () { - return `Listing ${util.inspect(Array.from(this))}`; -}); - -export default Listing; diff --git a/src/objects/Listing.ts b/src/objects/Listing.ts new file mode 100644 index 00000000..64b194a5 --- /dev/null +++ b/src/objects/Listing.ts @@ -0,0 +1,353 @@ +import util from 'util' +import {defineInspectFunc} from '../helper' +import {isBrowser, URL} from '../helpers' +import {InvalidMethodCallError} from '../errors' +import More, {emptyChildren} from './More' +import type snoowrap from '../snoowrap' +import type {RedditContent, Comment} from './' +import type {HTTP_VERBS} from '../constants' +import type {Children} from '../interfaces' + + +interface ListingQuery { + /** fullname of a thing */ + after?: string|null + /** fullname of a thing */ + before?: string|null + /** The maximum number of items desired */ + limit?: number + /** The string 'all' */ + show?: string + /** A positive integer */ + count?: number + [key: string]: any +} + +interface SortedListingQuery extends ListingQuery { + /** Describes the timespan that posts should be retrieved from */ + t?: 'all'|'hour'|'day'|'week'|'month'|'year' +} + +interface ListingProps { + _query: ListingQuery, + _transform: (value: any) => any, + _method: typeof HTTP_VERBS[number], + _isCommentList: boolean, + _link_id?: string, + _uri?: string, + _more?: More, + _cachedLookahead: Comment[] + _children: {[id: string]: Comment} +} + +interface Options extends Partial { + after: string|null + before: string|null + children: any[] + dist: number + geo_filter: string|null + modhash: string|null + [key: string]: any +} + +interface FetchMoreOptions { + amount: number + skipReplies: boolean + append: boolean + [key: string]: any +} + +interface FetchAllOptions { + skipReplies?: boolean + append?: boolean + [key: string]: any +} + +/** + * A class representing a list of content. This is a subclass of the native Array object, so it has all the properties of + * an Array (length, forEach, etc.) in addition to some added methods. The Listing can be extended by using the + * [#fetchMore()]{@link Listing#fetchMore} and [#fetchAll()]{@link Listing#fetchAll} functions. + * Note that these methods return new Listings, rather than mutating the original Listing. + * + * Most methods that return Listings will also accept `limit`, `after`, `before`, `show`, and `count` properties. + * + * If you've used the reddit API before (or used other API wrappers like [PRAW](https://praw.readthedocs.org/en/stable/)), you + * might know that reddit uses a `More` object in its raw JSON responses, representing comments that have been stubbed + * out of Listings. In snoowrap, there are no exposed `More` objects; the objects returned by the reddit API are + * stripped from Listings and are used internally as sources for the `fetchMore` functions. This means that in snoowrap, Listings + * that contain Comments can be used/expanded in the same manner as Listings that don't contain Comments, and for the most part + * you don't have to worry about the distinction. + * + * (Incidentally, if you encounter a Listing that *does* contain a `More` object then it's a bug, so please report it.) + * + * + * @extends Array + */ +interface Listing extends ListingProps {} +class Listing extends Array { + ['constructor']: typeof Listing + static _name = 'Listing' + + _r!: snoowrap + + constructor ( + { + _transform = value => value, + _method = 'get', + _isCommentList = false, + _cachedLookahead = [], + _children = {}, + children = [], + ...options + } : Partial = {}, + _r?: snoowrap, + public _hasFetched?: boolean + ) { + super() + if (!(this instanceof Listing)) { + /** + * Safari 9 has an incorrect implementation of classes that extend Arrays. As a workaround, + * manually set the constructor and prototype. + */ + // @ts-ignore + this.constructor = Listing + Object.setPrototypeOf(this, Listing.prototype) + } + this.push(...children || []) + this._r = _r! + + this._query = {...options._query} + if (options.after !== undefined) this._query.after = options.after + if (options.before !== undefined) this._query.before = options.before + + this._transform = _transform + this._method = _method + this._isCommentList = _isCommentList + this._link_id = options._link_id + this._uri = options._uri + this._more = options._more + this._cachedLookahead = _cachedLookahead + this._children = _children + + if (children && children[children.length - 1] instanceof More) { + this._setMore(children.pop()) + } + } + + _setMore (moreObj: More) { + this._more = moreObj + this._isCommentList = true + } + + _setUri (uri: string) { + const parsedUri = new URL(uri) + this._uri = parsedUri.pathname + parsedUri.searchParams.forEach((value, key) => { + // Why not just overriding all of them? + if (this._query[key] === undefined) this._query[key] = value + }) + if (parsedUri.searchParams.get('before')) { + this._query.after = null + } else { + this._query.before = null + } + } + + /** + * @summary A getter that indicates whether this Listing has any more items to fetch. + */ + get isFinished () { + // The process of checking whether a Listing is 'finished' varies depending on what kind of Listing it is. + return this._isCommentList + /** + * For comment Listings (i.e. Listings containing comments and comment replies, sourced by `More` objects): A Listing is + * *never* finished if it has a cached lookahead (i.e. extra items that were fetched from a previous request). If there is + * no cached lookahead, a Listing is finished iff it has an empty `More` object. + */ + ? !this._cachedLookahead.length && this._more && !this._more.children.length + /** + * For non-comment Listings: A Listing is always finished if it has no URI (since there would be nowhere to fetch items + * from). If it has a URI, a Listing is finished iff its `before` and `after` query are both `null`. This is because reddit + * returns a value of `null` as the `after` and `before` parameters to signify that a Listing is complete. + * + * It is important to check for `null` here rather than any falsey value, because when an empty Listing is initialized, its + * `after` and `before` properties are both `undefined`, but calling these empty Listings `finished` would be incorrect. + */ + : !this._uri || (this._query.after === null && this._query.before === null) + } + + /** + * @summary Fetches some more items + * @param options Object of fetching options or the number of items to fetch. + * @param options.amount The number of items to fetch. + * @param {boolean} [options.append=true] If `true`, the resulting Listing will contain the existing elements in addition to + * the newly-fetched elements. If `false`, the resulting Listing will only contain the newly-fetched elements. + * @param {boolean} [options.skipReplies=false] For a Listing that contains comment objects on a Submission, this option can + * be used to save a few API calls, provided that only top-level comments are being examined. If this is set to `true`, snoowrap + * is able to fetch 100 Comments per API call rather than 20, but all returned Comments will have no fetched replies by default. + * + * Internal details: When `skipReplies` is set to `true`, snoowrap uses reddit's `api/info` endpoint to fetch Comments. When + * `skipReplies` is set to `false`, snoowrap uses reddit's `api/morechildren` endpoint. It's worth noting that reddit does + * not allow concurrent requests to the `api/morechildren` endpoint by the same account. + * @returns A new Listing containing the newly-fetched elements. If `options.append` is `true`, the new Listing will + * also contain all elements that were in the original Listing. Under most circumstances, the newly-fetched elements will appear + * at the end of the new Listing. However, if reverse pagination is enabled (i.e. if this Listing was created with a `before` + * query parameter), then the newly-fetched elements will appear at the beginning. In any case, continuity is maintained, i.e. + * the order of items in the Listing will be the same as the order in which they appear on reddit. + * @example + * r.getHot({limit: 25}).then(myListing => { + * console.log(myListing.length) // => 25 + * myListing.fetchMore({amount: 10}).then(extendedListing => { + * console.log(extendedListing.length) // => 35 + * }) + * }) + */ + async fetchMore (options: Partial|number): Promise> { + const { + amount, + append = true, + skipReplies = false + } = typeof options === 'number' ? {amount: options} : options + if (typeof amount !== 'number' || Number.isNaN(amount)) { + throw new InvalidMethodCallError('Failed to fetch Listing. (\'amount\' parameter was missing or invalid)') + } + if (amount <= 0 || this.isFinished) { + return append ? this._clone() : this._clone()._empty() + } + if (this._cachedLookahead.length) { + const cloned = this._clone() + cloned.push(...cloned._cachedLookahead.splice(0, amount) as T[]) + return cloned.fetchMore(amount - cloned.length + this.length) + } + const parsedOptions = {amount, append, skipReplies} + return this._more ? this._fetchMoreComments(parsedOptions) : this._fetchMoreRegular(parsedOptions) + } + + /** + * @summary Fetches all of the items in this Listing, only stopping when there are none left. + * @param options Fetching options -- see {@link Listing#fetchMore} + * @returns A new fully-fetched Listing. Keep in mind that this method has the potential to exhaust your + * ratelimit quickly if the Listing doesn't have a clear end (e.g. with posts on the front page), so use it with discretion. + * @example + * + * r.getMe().getUpvotedContent().fetchAll().then(console.log) + * // => Listing [ Submission { ... }, Submission { ... }, ... ] + */ + fetchAll ({append = true, skipReplies = false}: FetchAllOptions = {}) { + return this.fetchMore({append, skipReplies, amount: Infinity}) + } + + async _fetchMoreRegular (options: FetchMoreOptions) { + const query: ListingProps['_query'] = {} + for (const key of Object.keys(this._query)) { + const value = this._query[key] + if (value !== null && value !== undefined) query[key] = value + } + /** + * Reddit returns a different number of items per request depending on the `limit` querystring property specified in the + * request. If no `limit` property is specified, reddit returns some number of items depending on the user's preferences + * (currently 25 items with default preferences). If a `limit` property is specified, then reddit returns `limit` items per + * batch. However, this is capped at 100, so if a `limit` larger than 100 items is specified, reddit will only return 100 + * items in the batch. (The cap of 100 could plausibly change to a different amount in the future.) + * + * However, one caveat is that reddit's parser doesn't understand the javascript `Infinity` global. If `limit=Infinity` is + * provided in the querystring, reddit won't understand the parameter so it'll just act as if no parameter was provided, and + * will return 25 items in the batch. This is suboptimal behavior as far as snoowrap is concerned, because it means that 4 + * times as many requests are needed to fetch the entire listing. + * + * To get around the issue, snoowrap caps the `limit` property at Number.MAX_SAFE_INTEGER when sending requests. This ensures + * that `Infinity` will never be sent as part of the querystring, so reddit will always return the maximal 100 items per + * request if the desired amount of items is large. + * + * Keep in mind that the `limit` property is only specified when fetching non-comment listings. Any extra items within comment + * listings will be sliced off and pushed to `this._cachedLookahead`. + */ + if (!this._isCommentList) query.limit = Math.min(options.amount, Number.MAX_SAFE_INTEGER) + let response: Listing = await this._r.oauthRequest({ + url: this._uri, + params: query, + method: this._method + }) + response = this._transform(response) + const cloned = this._clone() + if (!options.append) cloned._empty() + if (cloned._query.before) { + cloned.unshift(...response) + cloned._query.before = response._query.before + cloned._query.after = null + } else { + cloned.push(...response) + cloned._query.before = null + cloned._query.after = response._query.after + } + if (this._isCommentList) { + cloned._more = cloned._more || response._more || emptyChildren() + if (response.length > options.amount) { + cloned._cachedLookahead = Array.from(cloned.splice(options.amount)) as Comment[] + } + } + cloned._children = {...cloned._children, ...response._children} + return cloned.fetchMore({...options, append: true, amount: options.amount - response.length}) + } + + /** + * Pagination for comments works differently than it does for most other things; rather than sending a link to the next page + * within a Listing, reddit sends the last comment in the list as as a `more` object, with links to all the remaining comments + * in the thread. + */ + async _fetchMoreComments (options: FetchMoreOptions) { + if (!this._more) throw new InvalidMethodCallError('Failed to fetch more comments. (More object is missing)') + const moreComments = await this._more.fetchMore(options) + const children = moreComments._children + const cloned = this._clone() + if (!options.append) cloned._empty() + // Rebuild comments listing since Reddit doesn't return it in the right order sometimes + for (const id of cloned._more!.children.splice(0, options.amount)) { + const comment = children[id] + // Ignore comments removed from listing + if (comment) cloned.push(comment as T) + } + cloned._children = {...cloned._children, ...children} + cloned._more!.children = cloned._more!.children.slice(options.amount) + return cloned + } + + _empty () { + this.splice(0, this.length) + return this + } + + _clone (deep = false, _children: Children = {}) { + const properties: Partial = { + _query: {...this._query}, + _transform: this._transform, + _method: this._method, + _isCommentList: this._isCommentList, + _link_id: this._link_id, + _uri: this._uri, + _more: this._more && this._more._clone(), + _cachedLookahead: [...this._cachedLookahead] + } + if (!deep) { + properties._children = this._children + properties.children = Array.from(this) + return new Listing(properties, this._r) + } + properties._children = _children + properties.children = Array.from(this).map(item => item && item._clone ? item._clone(deep, _children) : item) + return new Listing(properties, this._r) + } + + toJSON () { + return Array.from(this).map(item => item && item.toJSON ? item.toJSON() : item) + } +} + +if (!isBrowser) { + defineInspectFunc(Listing.prototype, function (this: Listing) { // Fake param + return `Listing ${util.inspect(Array.from(this))}` + }) +} + +export default Listing +export {ListingProps, Options, FetchMoreOptions, FetchAllOptions, ListingQuery, SortedListingQuery} diff --git a/src/objects/LiveThread.d.ts b/src/objects/LiveThread.d.ts index 2d9c39d9..b7a2d4ef 100644 --- a/src/objects/LiveThread.d.ts +++ b/src/objects/LiveThread.d.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import {EventEmitter} from 'events'; import Listing, { ListingOptions } from './Listing'; import RedditContent from './RedditContent'; diff --git a/src/objects/LiveThread.js b/src/objects/LiveThread.js deleted file mode 100644 index 9c544f3e..00000000 --- a/src/objects/LiveThread.js +++ /dev/null @@ -1,287 +0,0 @@ -import {EventEmitter} from 'events'; -import {formatLivethreadPermissions, handleJsonErrors, isBrowser} from '../helpers.js'; -import RedditContent from './RedditContent.js'; - -const WebSocket = isBrowser ? global.WebSocket : require('ws'); - -const api_type = 'json'; - -/** -* A class representing a live reddit thread -* -* @example -* -* // Get a livethread with the given ID -* r.getLivethread('whrdxo8dg9n0') -* @desc For the most part, reddit distributes the content of live threads via websocket, rather than through the REST API. -As such, snoowrap assigns each fetched LiveThread object a `stream` property, which takes the form of an -[EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter). To listen for new thread updates, simply -add listeners to that emitter. - -The following events can be emitted: -- `update`: Occurs when a new update has been posted in this thread. Emits a `LiveUpdate` object containing information -about the new update. -- `activity`: Occurs periodically when the viewer count for this thread changes. -- `settings`: Occurs when the thread's settings change. Emits an object containing the new settings. -- `delete`: Occurs when an update has been deleted. Emits the ID of the deleted update. -- `strike`: Occurs when an update has been striken (marked incorrect and crossed out). Emits the ID of the striken update. -- `embeds_ready`: Occurs when embedded media is now available for a previously-posted update. -- `complete`: Occurs when this LiveThread has been marked as complete, and no more updates will be sent. - -(Note: These event types are mapped directly from reddit's categorization of the updates. The descriptions above are -paraphrased from reddit's descriptions [here](https://www.reddit.com/dev/api#section_live).) - -As an example, this would log all new livethread updates to the console: - -```javascript -someLivethread.stream.on('update', data => { - console.log(data.body); -}); -``` - -* @extends RedditContent -*/ -const LiveThread = class LiveThread extends RedditContent { - constructor (options, _r, _hasFetched) { - super(options, _r, _hasFetched); - this._rawStream = null; - this._populatedStream = null; - if (_hasFetched) { - Object.defineProperty(this, 'stream', {get: () => { - if (!this._populatedStream && this.websocket_url) { - this._setupWebSocket(); - } - return this._populatedStream; - }}); - } - } - get _uri () { - return `live/${this.id}/about`; - } - _setupWebSocket () { - this._rawStream = new WebSocket(this.websocket_url); - this._populatedStream = new EventEmitter(); - const handler = data => { - const parsed = this._r._populate(JSON.parse(data)); - this._populatedStream.emit(parsed.type, parsed.payload); - }; - if (typeof this._rawStream.on === 'function') { - this._rawStream.on('message', handler); - } else { - this._rawStream.onmessage = messageEvent => handler(messageEvent.data); - } - } - /** - * @summary Adds a new update to this thread. - * @param {string} body The body of the new update - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').addUpdate('Breaking: Someone is reading the snoowrap documentation \\o/') - */ - addUpdate (body) { - return this._post({uri: `api/live/${this.id}/update`, form: {api_type, body}}).then(handleJsonErrors(this)); - } - /** - * @summary Strikes (marks incorrect and crosses out) the given update. - * @param {object} options - * @param {string} options.id The ID of the update that should be striked. - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').strikeUpdate({id: 'LiveUpdate_edc34446-faf0-11e5-a1b4-0e858bca33cd'}) - */ - strikeUpdate ({id}) { - return this._post({ - uri: `api/live/${this.id}/strike_update`, - form: {api_type, id: `${id.startsWith('LiveUpdate_') ? '' : 'LiveUpdate_'}${id}`} - }).then(handleJsonErrors(this)); - } - /** - * @summary Deletes an update from this LiveThread. - * @param {object} options - * @param {string} options.id The ID of the LiveUpdate that should be deleted - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').deleteUpdate({id: 'LiveUpdate_edc34446-faf0-11e5-a1b4-0e858bca33cd'}) - */ - deleteUpdate ({id}) { - return this._post({ - uri: `api/live/${this.id}/delete_update`, - form: {api_type, id: `${id.startsWith('LiveUpdate_') ? '' : 'LiveUpdate_'}${id}`} - }).then(handleJsonErrors(this)); - } - /** - * @summary Gets a list of this LiveThread's contributors - * @returns {Promise} An Array containing RedditUsers - * @example - * - * r.getLivethread('whrdxo8dg9n0').getContributors().then(console.log) - * // => [ - * // RedditUser { permissions: ['edit'], name: 'not_an_aardvark', id: 't2_k83md' }, - * // RedditUser { permissions: ['all'], id: 't2_u3l80', name: 'snoowrap_testing' } - * // ] - */ - getContributors () { - return this._get({uri: `live/${this.id}/contributors`}).then(contributors => { - return Array.isArray(contributors[0]) ? contributors[0] : contributors; - }); - } - /** - * @summary Invites a contributor to this LiveThread. - * @param {object} options - * @param {string} options.name The name of the user who should be invited - * @param {Array} options.permissions The permissions that the invited user should receive. This should be an Array containing - some combination of `'update', 'edit', 'manage'`. To invite a contributor with full permissions, omit this property. - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').inviteContributor({name: 'actually_an_aardvark', permissions: ['update']}) - */ - inviteContributor ({name, permissions}) { - return this._post({uri: `api/live/${this.id}/invite_contributor`, form: { - api_type, - name, - permissions: formatLivethreadPermissions(permissions), - type: 'liveupdate_contributor_invite' - }}).then(handleJsonErrors(this)); - } - /** - * @summary Revokes an invitation for the given user to become a contributor on this LiveThread. - * @param {object} options - * @param {string} options.name The username of the account whose invitation should be revoked - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').revokeContributorInvite({name: 'actually_an_aardvark'}); - */ - revokeContributorInvite ({name}) { - return this._r.getUser(name).fetch().get('id').then(userId => { - return this._post({uri: `api/live/${this.id}/rm_contributor_invite`, form: {api_type, id: `t2_${userId}`}}); - }).then(handleJsonErrors(this)); - } - /** - * @summary Accepts a pending contributor invitation on this LiveThread. - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').acceptContributorInvite() - */ - acceptContributorInvite () { - return this._post({uri: `api/live/${this.id}/accept_contributor_invite`, form: {api_type}}).return(this); - } - /** - * @summary Abdicates contributor status on this LiveThread. - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').leaveContributor() - */ - leaveContributor () { - return this._post({uri: `api/live/${this.id}/leave_contributor`, form: {api_type}}).return(this); - } - /** - * @summary Removes the given user from contributor status on this LiveThread. - * @param {object} options - * @param {string} options.name The username of the account who should be removed - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').removeContributor({name: 'actually_an_aardvark'}) - */ - removeContributor ({name}) { - return this._r.getUser(name).fetch().get('id').then(userId => { - return this._post({uri: `api/live/${this.id}/rm_contributor`, form: {api_type, id: `t2_${userId}`}}); - }).then(handleJsonErrors(this)); - } - /** - * @summary Sets the permissions of the given contributor. - * @param {object} options - * @param {string} options.name The name of the user whose permissions should be changed - * @param {Array} options.permissions The updated permissions that the user should have. This should be an Array containing - some combination of `'update', 'edit', 'manage'`. To give the contributor with full permissions, omit this property. - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').setContributorPermissions({name: 'actually_an_aardvark', permissions: ['edit']}) - */ - setContributorPermissions ({name, permissions}) { - return this._post({ - uri: `api/live/${this.id}/set_contributor_permissions`, - form: {api_type, name, permissions: formatLivethreadPermissions(permissions), type: 'liveupdate_contributor'} - }).then(handleJsonErrors(this)); - } - /** - * @summary Edits the settings on this LiveThread. - * @param {object} options - * @param {string} options.title The title of the thread - * @param {string} [options.description] A descriptions of the thread. 120 characters max - * @param {string} [options.resources] Information and useful links related to the thread. - * @param {boolean} options.nsfw Determines whether the thread is Not Safe For Work - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').editSettings({title: 'My livethread', description: 'an updated description'}) - */ - editSettings ({title, description, resources, nsfw}) { - return this._post({ - uri: `api/live/${this.id}/edit`, - form: {api_type, description, nsfw, resources, title} - }).then(handleJsonErrors(this)); - } - /** - * @summary Permanently closes this thread, preventing any more updates from being added. - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').closeThread() - */ - closeThread () { - return this._post({uri: `api/live/${this.id}/close_thread`, form: {api_type}}).return(this); - } - /** - * @summary Reports this LiveThread for breaking reddit's rules. - * @param {object} options - * @param {string} options.reason The reason for the report. One of `spam`, `vote-manipulation`, `personal-information`, - `sexualizing-minors`, `site-breaking` - * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete - * @example r.getLivethread('whrdxo8dg9n0').report({reason: 'Breaking a rule blah blah blah'}) - */ - report ({reason}) { - return this._post({uri: `api/live/${this.id}/report`, form: {api_type, type: reason}}).then(handleJsonErrors(this)); - } - /** - * @summary Gets a Listing containing past updates to this LiveThread. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing LiveUpdates - * @example - * - * r.getLivethread('whrdxo8dg9n0').getRecentUpdates().then(console.log) - * // => Listing [ - * // LiveUpdate { ... }, - * // LiveUpdate { ... }, - * // ... - * // ] - */ - getRecentUpdates (options) { - return this._getListing({uri: `live/${this.id}`, qs: options}); - } - /** - * @summary Gets a list of reddit submissions linking to this LiveThread. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions - * @example - * - * r.getLivethread('whrdxo8dg9n0').getDiscussions().then(console.log) - * // => Listing [ - * // Submission { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getDiscussions (options) { - return this._getListing({uri: `live/${this.id}/discussions`, qs: options}); - } - /** - * @summary Stops listening for new updates on this LiveThread. - * @desc To avoid memory leaks that can result from open sockets, it's recommended that you call this method when you're - finished listening for updates on this LiveThread. - * - * This should not be confused with {@link LiveThread#closeThread}, which marks the thread as "closed" on reddit. - * @returns undefined - * @example - * - * var myThread = r.getLivethread('whrdxo8dg9n0'); - * myThread.stream.on('update', content => { - * console.log(content); - * myThread.closeStream(); - * }) - * - */ - closeStream () { - if (this._rawStream) { - this._rawStream.close(); - } - } -}; - -export default LiveThread; diff --git a/src/objects/LiveThread.ts b/src/objects/LiveThread.ts new file mode 100644 index 00000000..51844e6a --- /dev/null +++ b/src/objects/LiveThread.ts @@ -0,0 +1,306 @@ +import {EventEmitter} from 'events'; +import {formatLivethreadPermissions, handleJsonErrors, isBrowser} from '../helper'; +import RedditContent from './RedditContent'; +// +const WebSocket = isBrowser ? self.WebSocket : require('ws'); + +const api_type = 'json'; + +/** + * A class representing a live reddit thread + * @example + * + * // Get a livethread with the given ID + * r.getLivethread('whrdxo8dg9n0') + * @desc For the most part, reddit distributes the content of live threads via websocket, rather than through the REST API. + * As such, snoowrap assigns each fetched LiveThread object a `stream` property, which takes the form of an + * [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter). To listen for new thread updates, simply + * add listeners to that emitter. + * + * The following events can be emitted: + * - `update`: Occurs when a new update has been posted in this thread. Emits a `LiveUpdate` object containing information + * about the new update. + * - `activity`: Occurs periodically when the viewer count for this thread changes. + * - `settings`: Occurs when the thread's settings change. Emits an object containing the new settings. + * - `delete`: Occurs when an update has been deleted. Emits the ID of the deleted update. + * - `strike`: Occurs when an update has been striken (marked incorrect and crossed out). Emits the ID of the striken update. + * - `embeds_ready`: Occurs when embedded media is now available for a previously-posted update. + * - `complete`: Occurs when this LiveThread has been marked as complete, and no more updates will be sent. + * + * (Note: These event types are mapped directly from reddit's categorization of the updates. The descriptions above are + * paraphrased from reddit's descriptions [here](https://www.reddit.com/dev/api#section_live).) + * + * As an example, this would log all new livethread updates to the console: + * + * ```javascript + * someLivethread.stream.on('update', data => { + * console.log(data.body); + * }); + * ``` + * + * @extends RedditContent + */ +class LiveThread extends RedditContent { + static _name = 'LiveThread' + + constructor (options, _r, _hasFetched) { + super(options, _r, _hasFetched); + this._rawStream = null; + this._populatedStream = null; + if (_hasFetched) { + Object.defineProperty(this, 'stream', {get: () => { + if (!this._populatedStream && this.websocket_url) { + this._setupWebSocket(); + } + return this._populatedStream; + }}); + } + } + get _uri () { + return `live/${this.id}/about`; + } + _setupWebSocket () { + this._rawStream = new WebSocket(this.websocket_url); + this._populatedStream = new EventEmitter(); + const handler = data => { + const parsed = this._r._populate(JSON.parse(data)); + this._populatedStream.emit(parsed.type, parsed.payload); + }; + if (typeof this._rawStream.on === 'function') { + this._rawStream.on('message', handler); + } else { + this._rawStream.onmessage = messageEvent => handler(messageEvent.data); + } + } + /** + * @summary Adds a new update to this thread. + * @param {string} body The body of the new update + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').addUpdate('Breaking: Someone is reading the snoowrap documentation \\o/') + */ + async addUpdate (body) { + const res = await this._post({url: `api/live/${this.id}/update`, form: {api_type, body}}); + handleJsonErrors(res); + return this; + } + /** + * @summary Strikes (marks incorrect and crosses out) the given update. + * @param {object} options + * @param {string} options.id The ID of the update that should be striked. + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').strikeUpdate({id: 'LiveUpdate_edc34446-faf0-11e5-a1b4-0e858bca33cd'}) + */ + async strikeUpdate ({id}) { + const res = await this._post({ + url: `api/live/${this.id}/strike_update`, + form: {api_type, id: `${id.startsWith('LiveUpdate_') ? '' : 'LiveUpdate_'}${id}`} + }); + handleJsonErrors(res); + return this; + } + /** + * @summary Deletes an update from this LiveThread. + * @param {object} options + * @param {string} options.id The ID of the LiveUpdate that should be deleted + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').deleteUpdate({id: 'LiveUpdate_edc34446-faf0-11e5-a1b4-0e858bca33cd'}) + */ + async deleteUpdate ({id}) { + const res = await this._post({ + url: `api/live/${this.id}/delete_update`, + form: {api_type, id: `${id.startsWith('LiveUpdate_') ? '' : 'LiveUpdate_'}${id}`} + }); + handleJsonErrors(res); + return this; + } + /** + * @summary Gets a list of this LiveThread's contributors + * @returns {Promise} An Array containing RedditUsers + * @example + * + * r.getLivethread('whrdxo8dg9n0').getContributors().then(console.log) + * // => [ + * // RedditUser { permissions: ['edit'], name: 'not_an_aardvark', id: 't2_k83md' }, + * // RedditUser { permissions: ['all'], id: 't2_u3l80', name: 'snoowrap_testing' } + * // ] + */ + async getContributors () { + const contributors = await this._get({url: `live/${this.id}/contributors`}); + return Array.isArray(contributors[0]) ? contributors[0] : contributors; + } + /** + * @summary Invites a contributor to this LiveThread. + * @param {object} options + * @param {string} options.name The name of the user who should be invited + * @param {Array} options.permissions The permissions that the invited user should receive. This should be an Array containing + * some combination of `'update', 'edit', 'manage'`. To invite a contributor with full permissions, omit this property. + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').inviteContributor({name: 'actually_an_aardvark', permissions: ['update']}) + */ + async inviteContributor ({name, permissions}) { + const res = await this._post({url: `api/live/${this.id}/invite_contributor`, form: { + api_type, + name, + permissions: formatLivethreadPermissions(permissions), + type: 'liveupdate_contributor_invite' + }}); + handleJsonErrors(res); + return this; + } + /** + * @summary Revokes an invitation for the given user to become a contributor on this LiveThread. + * @param {object} options + * @param {string} options.name The username of the account whose invitation should be revoked + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').revokeContributorInvite({name: 'actually_an_aardvark'}); + */ + async revokeContributorInvite ({name}) { + const userId = (await this._r.getUser(name).fetch()).id; + const res = await this._post({url: `api/live/${this.id}/rm_contributor_invite`, form: {api_type, id: `t2_${userId}`}}); + handleJsonErrors(res); + return this; + } + /** + * @summary Accepts a pending contributor invitation on this LiveThread. + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').acceptContributorInvite() + */ + async acceptContributorInvite () { + await this._post({url: `api/live/${this.id}/accept_contributor_invite`, form: {api_type}}); + return this; + } + /** + * @summary Abdicates contributor status on this LiveThread. + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').leaveContributor() + */ + async leaveContributor () { + await this._post({url: `api/live/${this.id}/leave_contributor`, form: {api_type}}); + return this; + } + /** + * @summary Removes the given user from contributor status on this LiveThread. + * @param {object} options + * @param {string} options.name The username of the account who should be removed + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').removeContributor({name: 'actually_an_aardvark'}) + */ + async removeContributor ({name}) { + const userId = (await this._r.getUser(name).fetch()).id; + const res = await this._post({url: `api/live/${this.id}/rm_contributor`, form: {api_type, id: `t2_${userId}`}}); + handleJsonErrors(res); + return this; + } + /** + * @summary Sets the permissions of the given contributor. + * @param {object} options + * @param {string} options.name The name of the user whose permissions should be changed + * @param {Array} options.permissions The updated permissions that the user should have. This should be an Array containing + * some combination of `'update', 'edit', 'manage'`. To give the contributor with full permissions, omit this property. + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').setContributorPermissions({name: 'actually_an_aardvark', permissions: ['edit']}) + */ + async setContributorPermissions ({name, permissions}) { + const res = await this._post({ + url: `api/live/${this.id}/set_contributor_permissions`, + form: {api_type, name, permissions: formatLivethreadPermissions(permissions), type: 'liveupdate_contributor'} + }); + handleJsonErrors(res); + return this; + } + /** + * @summary Edits the settings on this LiveThread. + * @param {object} options + * @param {string} options.title The title of the thread + * @param {string} [options.description] A descriptions of the thread. 120 characters max + * @param {string} [options.resources] Information and useful links related to the thread. + * @param {boolean} options.nsfw Determines whether the thread is Not Safe For Work + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').editSettings({title: 'My livethread', description: 'an updated description'}) + */ + async editSettings ({title, description, resources, nsfw}) { + const res = await this._post({ + url: `api/live/${this.id}/edit`, + form: {api_type, description, nsfw, resources, title} + }); + handleJsonErrors(res); + return this; + } + /** + * @summary Permanently closes this thread, preventing any more updates from being added. + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').closeThread() + */ + async closeThread () { + await this._post({url: `api/live/${this.id}/close_thread`, form: {api_type}}); + return this; + } + /** + * @summary Reports this LiveThread for breaking reddit's rules. + * @param {object} options + * @param {string} options.reason The reason for the report. One of `spam`, `vote-manipulation`, `personal-information`, + * `sexualizing-minors`, `site-breaking` + * @returns {Promise} A Promise that fulfills with this LiveThread when the request is complete + * @example r.getLivethread('whrdxo8dg9n0').report({reason: 'Breaking a rule blah blah blah'}) + */ + async report ({reason}) { + const res = await this._post({url: `api/live/${this.id}/report`, form: {api_type, type: reason}}); + handleJsonErrors(res); + return this; + } + /** + * @summary Gets a Listing containing past updates to this LiveThread. + * @param {object} [options] Options for the resulting Listing + * @returns {Promise} A Listing containing LiveUpdates + * @example + * + * r.getLivethread('whrdxo8dg9n0').getRecentUpdates().then(console.log) + * // => Listing [ + * // LiveUpdate { ... }, + * // LiveUpdate { ... }, + * // ... + * // ] + */ + getRecentUpdates (options) { + return this._getListing({uri: `live/${this.id}`, qs: options}); + } + /** + * @summary Gets a list of reddit submissions linking to this LiveThread. + * @param {object} [options] Options for the resulting Listing + * @returns {Promise} A Listing containing Submissions + * @example + * + * r.getLivethread('whrdxo8dg9n0').getDiscussions().then(console.log) + * // => Listing [ + * // Submission { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getDiscussions (options) { + return this._getListing({uri: `live/${this.id}/discussions`, qs: options}); + } + /** + * @summary Stops listening for new updates on this LiveThread. + * @desc To avoid memory leaks that can result from open sockets, it's recommended that you call this method when you're + * finished listening for updates on this LiveThread. + * + * This should not be confused with {@link LiveThread#closeThread}, which marks the thread as "closed" on reddit. + * @returns undefined + * @example + * + * var myThread = r.getLivethread('whrdxo8dg9n0'); + * myThread.stream.on('update', content => { + * console.log(content); + * myThread.closeStream(); + * }) + * + */ + closeStream () { + if (this._rawStream) { + this._rawStream.close(); + } + } +}; + +export default LiveThread; diff --git a/src/objects/MediaFile.ts b/src/objects/MediaFile.ts new file mode 100644 index 00000000..ab4d7a32 --- /dev/null +++ b/src/objects/MediaFile.ts @@ -0,0 +1,93 @@ +/** + * @summary An Interface representing parameters to pass to the {@link MediaFile} constructor. + */ +export interface FileDetails { + /** + * @summary A direct URL to the uploaded file. Used to embed the file on image/video submissions. + */ + fileUrl: string + /** + * @summary A unique identifier for the uploaded file. Used to embed the file on selfposts and galleries. + */ + mediaId: string + /** + * @summary The websocket URL can be used to determine when media processing is finished and to obtain the newly created submission ID. + */ + websocketUrl?: string + /** + * @summary A caption for the embedded file to be used on selfposts bodies and gallery items. + * @desc **NOTE**: Captions on gallery items must be 180 characters or less. + */ + caption?: string + /** + * @summary An external URL to be used on gallery items. + */ + outboundUrl?: string +} + +/** + * A class representing media files uploaded to reddit to be embedded on submissions. See {@link snoowrap#uploadMedia} for more details. + */ +interface MediaFile extends FileDetails {} +class MediaFile { + /** + * @summary The media type. Only available on {@link MediaImg}, {@link MediaVideo} and {@link MediaGif}. + */ + type?: string + + /** + * @summary Constructs a new media file. In most cases you should call {@link snoowrap#uploadMedia} instead. + * @param options An object containing `fileUrl`, `mediaId` along with optional `websocketUrl`, `caption` and `outboundUrl`. + */ + constructor ({fileUrl, mediaId, websocketUrl, caption, outboundUrl}: FileDetails) { + this.fileUrl = fileUrl + this.mediaId = mediaId + this.websocketUrl = websocketUrl + this.caption = caption + this.outboundUrl = outboundUrl + } + + /** + * @summary This method allows to embed media files on selfposts bodies. This only works with {@link MediaImg}, {@link MediaVideo} + * and {@link MediaGif} where the `type` property is set. + * @desc **NOTE**: Embedded media will have a padding of `\n\n` automatically added. This is due to the weirdness with Reddit's API. + * @returns {string} A string representation of the media file in Markdown format. + * @example + * + * const mediaImg = await r.uploadMedia({ // Usage as a `MediaImg`. + * file: './image.png', + * type: 'img', + * caption: 'optional caption' + * }) + * + * const body = `This is an inline image: ${mediaImg} Cool huh?` + * => "This is an inline image: \n\n![img](qwertyuiop \"optional caption\")\n\n Cool huh?" + */ + toString (): string { + return this.type ? `\n\n![${this.type}](${this.mediaId} "${this.caption ? this.caption : ''}")\n\n` : '' + } +} + +/** + * A subclass of {@link MediaFile} representing an image. The only difference is that the `type` property is set to `img` + */ +class MediaImg extends MediaFile { + type = 'img' +} + +/** + * A subclass of {@link MediaFile} representing a video. The only difference is that the `type` property is set to `video` + */ +class MediaVideo extends MediaFile { + type = 'video' +} + +/** + * A subclass of {@link MediaFile} representing a videogif. The only difference is that the `type` property is set to `gif` + */ +class MediaGif extends MediaVideo { + type = 'gif' +} + +export default MediaFile +export {MediaImg, MediaVideo, MediaGif} diff --git a/src/objects/ModmailConversation.d.ts b/src/objects/ModmailConversation.d.ts index 04936058..77b77ee7 100644 --- a/src/objects/ModmailConversation.d.ts +++ b/src/objects/ModmailConversation.d.ts @@ -1,90 +1,92 @@ +// @ts-nocheck import Subreddit from './Subreddit'; import RedditContent from './RedditContent'; import RedditUser from './RedditUser'; import ModmailConversationAuthor from './ModmailConversationAuthor'; export enum conversationStates { - New = 0, - InProgress = 1, - Archived = 2, + New = 0, + InProgress = 1, + Archived = 2, } export enum modActionStates { - Highlight = 0, - UnHighlight = 1, - Archive = 2, - UnArchive = 3, - ReportedToAdmins = 4, - Mute = 5, - Unmute = 6, + Highlight = 0, + UnHighlight = 1, + Archive = 2, + UnArchive = 3, + ReportedToAdmins = 4, + Mute = 5, + Unmute = 6, } export interface ModmailMessage { - body: string; - bodyMarkdown: string; - author: RedditUser; - isInternal: boolean; - date: string; - id: string; + body: string; + bodyMarkdown: string; + author: RedditUser; + isInternal: boolean; + date: string; + id: string; } export interface Author { - isMod: boolean; - isAdmin: boolean; - name: string; - isOp: boolean; - isParticipant: boolean; - isHidden: boolean; - id: any; - isDeleted: boolean; + isMod: boolean; + isAdmin: boolean; + name: string; + isOp: boolean; + isParticipant: boolean; + isHidden: boolean; + id: any; + isDeleted: boolean; } export interface Owner { - displayName: string; - type: string; - id: string; + displayName: string; + type: string; + id: string; } export interface ObjId { - id: string; - key: string; + id: string; + key: string; } export default class ModmailConversation extends RedditContent { - static conversationStates: conversationStates; - static modActionStats: modActionStates; + static conversationStates: conversationStates; + static modActionStats: modActionStates; + static _getConversationObjects(conversation, response): any - isAuto: boolean; - objIds: ObjId[]; - isRepliable: boolean; - lastUserUpdate?: any; - isInternal: boolean; - lastModUpdate: Date; - lastUpdated: Date; - authors: Author[]; - // sometimes an Owner, sometimes a Subreddit - owner: Owner | Subreddit; - id: string; - isHighlighted: boolean; - subject: string; - participant: ModmailConversationAuthor; - state: number; - lastUnread?: any; - numMessages: number; - messages?: ModmailMessage[]; + isAuto: boolean; + objIds: ObjId[]; + isRepliable: boolean; + lastUserUpdate?: any; + isInternal: boolean; + lastModUpdate: Date; + lastUpdated: Date; + authors: Author[]; + // sometimes an Owner, sometimes a Subreddit + owner: Owner | Subreddit; + id: string; + isHighlighted: boolean; + subject: string; + participant: ModmailConversationAuthor; + state: number; + lastUnread?: any; + numMessages: number; + messages?: ModmailMessage[]; - reply(body: string, isAuthorHidden?: boolean, isInternal?: boolean): Promise + reply(body: string, isAuthorHidden?: boolean, isInternal?: boolean): Promise - getParticipant(): Promise; + getParticipant(): Promise; - isRead(): boolean; + isRead(): boolean; - read(): Promise; - unread(): Promise; - mute(): Promise; - unmute(): Promise; - highlight(): Promise; - unhighlight(): Promise; - archive(): Promise; - unarchive(): Promise; + read(): Promise; + unread(): Promise; + mute(): Promise; + unmute(): Promise; + highlight(): Promise; + unhighlight(): Promise; + archive(): Promise; + unarchive(): Promise; } diff --git a/src/objects/ModmailConversation.js b/src/objects/ModmailConversation.ts similarity index 86% rename from src/objects/ModmailConversation.js rename to src/objects/ModmailConversation.ts index 45bcb759..26489b56 100644 --- a/src/objects/ModmailConversation.js +++ b/src/objects/ModmailConversation.ts @@ -1,5 +1,5 @@ -import RedditContent from './RedditContent.js'; - +import RedditContent from './RedditContent'; +// /** * @global * @enum {number} @@ -34,7 +34,6 @@ export const modActionStates = Object.freeze({ /** * @class * A class representing a conversation from new modmail - * * @name ModmailConversation * @example * @@ -42,7 +41,9 @@ export const modActionStates = Object.freeze({ * r.getNewModmailConversation('75hxt') * @extends RedditContent */ -const ModmailConversation = class ModmailConversation extends RedditContent { +class ModmailConversation extends RedditContent { + static _name = 'ModmailConversation' + static get conversationStates () { return conversationStates; } @@ -105,7 +106,7 @@ const ModmailConversation = class ModmailConversation extends RedditContent { */ reply (body, isAuthorHidden = false, isInternal = false) { return this._post({ - uri: `api/mod/conversations/${this.id}`, + url: `api/mod/conversations/${this.id}`, form: { body, isAuthorHidden, @@ -122,7 +123,7 @@ const ModmailConversation = class ModmailConversation extends RedditContent { * r.getNewModmailConversation('75hxt').archive() */ archive () { - return this._post({uri: `api/mod/conversations/${this.id}/archive`}); + return this._post({url: `api/mod/conversations/${this.id}/archive`}); } /** @@ -133,7 +134,7 @@ const ModmailConversation = class ModmailConversation extends RedditContent { * r.getNewModmailConversation('75hxt').unarchive() */ unarchive () { - return this._post({uri: `api/mod/conversations/${this.id}/unarchive`}); + return this._post({url: `api/mod/conversations/${this.id}/unarchive`}); } /** @@ -144,7 +145,7 @@ const ModmailConversation = class ModmailConversation extends RedditContent { * r.getNewModmailConversation('75hxt').highlight() */ highlight () { - return this._post({uri: `api/mod/conversations/${this.id}/highlight`}); + return this._post({url: `api/mod/conversations/${this.id}/highlight`}); } /** @@ -155,7 +156,7 @@ const ModmailConversation = class ModmailConversation extends RedditContent { * r.getNewModmailConversation('75hxt').unhighlight() */ unhighlight () { - return this._delete({uri: `api/mod/conversations/${this.id}/highlight`}); + return this._delete({url: `api/mod/conversations/${this.id}/highlight`}); } /** @@ -166,7 +167,7 @@ const ModmailConversation = class ModmailConversation extends RedditContent { * r.getNewModmailConversation('75hxt').mute() */ mute () { - return this._post({uri: `api/mod/conversations/${this.id}/mute`}); + return this._post({url: `api/mod/conversations/${this.id}/mute`}); } /** @@ -177,7 +178,7 @@ const ModmailConversation = class ModmailConversation extends RedditContent { * r.getNewModmailConversation('75hxt').unmute() */ unmute () { - return this._post({uri: `api/mod/conversations/${this.id}/unmute`}); + return this._post({url: `api/mod/conversations/${this.id}/unmute`}); } /** @@ -210,11 +211,9 @@ const ModmailConversation = class ModmailConversation extends RedditContent { * r.getNewModmailConversation('75hxt').getParticipant().then(console.log) * // ModmailConversationAuthor { muteStatus: {...}, name: "SpyTec13", created: '2015-11-22T14:30:38.821292+00:00', ...} */ - getParticipant () { - return this._get({uri: `api/mod/conversations/${this.id}/user`}) - .then(res => { - return this._r._newObject('ModmailConversationAuthor', res, true); - }); + async getParticipant () { + const res = await this._get({url: `api/mod/conversations/${this.id}/user`}); + return this._r._newObject('ModmailConversationAuthor', res, true); } /** diff --git a/src/objects/ModmailConversationAuthor.ts b/src/objects/ModmailConversationAuthor.ts index 703aa663..4b02481f 100644 --- a/src/objects/ModmailConversationAuthor.ts +++ b/src/objects/ModmailConversationAuthor.ts @@ -1,7 +1,8 @@ +// @ts-nocheck import RedditContent from './RedditContent'; import RedditUser from './RedditUser'; -import * as Snoowrap from '../snoowrap'; - +import snoowrap from '../snoowrap'; +// export interface BanStatus { endDate?: string | null; reason: string; @@ -31,7 +32,6 @@ export interface RecentComment { /** * A class representing an author from a modmail conversation - * * @example * * // Get a Modmail Conversation author with a given ID @@ -39,6 +39,8 @@ export interface RecentComment { * @extends RedditContent */ export default class ModmailConversationAuthor extends RedditContent { + static _name = 'ModmailConversationAuthor' + name!: string; isMod?: boolean; isAdmin?: boolean; @@ -55,7 +57,7 @@ export default class ModmailConversationAuthor extends RedditContent this._r._newObject('Comment', { @@ -82,4 +84,4 @@ export default class ModmailConversationAuthor extends RedditContent { return this._r.getUser(this.name); } -}; +} diff --git a/src/objects/More.js b/src/objects/More.js deleted file mode 100644 index 5ce71ca1..00000000 --- a/src/objects/More.js +++ /dev/null @@ -1,84 +0,0 @@ -import {concat, flatten, forEach, pick, remove} from 'lodash'; -import Promise from '../Promise.js'; -import {addEmptyRepliesListing, buildRepliesTree, handleJsonErrors} from '../helpers.js'; -import {MAX_API_INFO_AMOUNT, MAX_API_MORECHILDREN_AMOUNT} from '../constants.js'; - -const api_type = 'json'; - -/** -* The `More` class is a helper representing reddit's exposed `more` type in comment threads, used to fetch additional comments -on a thread. -* No instances of the `More` class are exposed externally by snoowrap; instead, comment lists are exposed as Listings. -Additional replies on an item can be fetched by calling `fetchMore` on a Listing, in the same manner as what would be done -with a Listing of posts. snoowrap should handle the differences internally, and expose a nearly-identical interface for the -two use-cases. - -Combining reddit's `Listing` and `more` objects has the advantage of having a more consistent exposed interface; for example, -if a consumer iterates over the comments on a Submission, all of the iterated items will actually be Comment objects, so the -consumer won't encounter an unexpected `more` object at the end. However, there are a few disadvantages, namely that (a) this -leads to an increase in internal complexity, and (b) there are a few cases where reddit's `more` objects have different amounts -of available information (e.g. all the child IDs of a `more` object are known on creation), which leads to different optimal -behavior. -*/ - -const More = class More { - constructor (options, _r) { - Object.assign(this, options); - this._r = _r; - } - /* Requests to /api/morechildren are capped at 20 comments at a time, but requests to /api/info are capped at 100, so - it's easier to send to the latter. The disadvantage is that comment replies are not automatically sent from requests - to /api/info. */ - fetchMore (options, startIndex = 0) { - if (options.amount <= 0 || startIndex >= this.children.length) { - return Promise.resolve([]); - } - if (!options.skipReplies) { - return this.fetchTree(options, startIndex); - } - const ids = getNextIdSlice(this.children, startIndex, options.amount, MAX_API_INFO_AMOUNT).map(id => `t1_${id}`); - // Requests are capped at 100 comments. Send lots of requests recursively to get the comments, then concatenate them. - // (This speed-requesting is only possible with comment Listings since the entire list of ids is present initially.) - const promiseForThisBatch = this._r._getListing({uri: 'api/info', qs: {id: ids.join(',')}}); - const nextRequestOptions = {...options, amount: options.amount - ids.length}; - const promiseForRemainingItems = this.fetchMore(nextRequestOptions, startIndex + ids.length); - return Promise.all([promiseForThisBatch, promiseForRemainingItems]).then(flatten); - } - fetchTree (options, startIndex) { - if (options.amount <= 0 || startIndex >= this.children.length) { - return Promise.resolve([]); - } - const ids = getNextIdSlice(this.children, startIndex, options.amount, MAX_API_MORECHILDREN_AMOUNT); - return this._r._get({ - uri: 'api/morechildren', - qs: {api_type, children: ids.join(','), link_id: this.link_id || this.parent_id} - }).tap(handleJsonErrors) - .then(res => res.json.data.things) - .map(addEmptyRepliesListing) - .then(buildRepliesTree) - .then(resultTrees => { - /* Sometimes, when sending a request to reddit to get multiple comments from a `more` object, reddit decides to only - send some of the requested comments, and then stub out the remaining ones in a smaller `more` object. ( ¯\_(ツ)_/¯ ) - In these cases, recursively fetch the smaller `more` objects as well. */ - const childMores = remove(resultTrees, c => c instanceof More); - forEach(childMores, c => { - c.link_id = this.link_id || this.parent_id; - }); - return Promise.mapSeries(childMores, c => c.fetchTree({...options, amount: Infinity}, 0)).then(expandedTrees => { - return this.fetchMore({...options, amount: options.amount - ids.length}, startIndex + ids.length).then(nexts => { - return concat(resultTrees, flatten(expandedTrees), nexts); - }); - }); - }); - } - _clone () { - return new More(pick(this, Object.getOwnPropertyNames(this)), this._r); - } -}; - -function getNextIdSlice (children, startIndex, desiredAmount, limit) { - return children.slice(startIndex, startIndex + Math.min(desiredAmount, limit)); -} - -export const emptyChildren = new More({children: []}); -export default More; diff --git a/src/objects/More.ts b/src/objects/More.ts new file mode 100644 index 00000000..e81aeca3 --- /dev/null +++ b/src/objects/More.ts @@ -0,0 +1,161 @@ +import {buildRepliesTree, handleJsonErrors} from '../helper' +import {MAX_API_INFO_AMOUNT, MAX_API_MORECHILDREN_AMOUNT} from '../constants' +import Listing, {type FetchMoreOptions} from './Listing' +import type snoowrap from '../snoowrap' +import type Comment from './Comment' +import type {JSONResponse} from '../interfaces' + +interface Options { + children: string[] + count: number + depth: number + id: string + name: string + parent_id: string + /** Custom */ + link_id?: string +} + +/** + * The `More` class is a helper representing reddit's exposed `more` type in comment threads, used to fetch additional comments + * on a thread. + * No instances of the `More` class are exposed externally by snoowrap; instead, comment lists are exposed as Listings. + * Additional replies on an item can be fetched by calling `fetchMore` on a Listing, in the same manner as what would be done + * with a Listing of posts. snoowrap should handle the differences internally, and expose a nearly-identical interface for the + * two use-cases. + * + * Combining reddit's `Listing` and `more` objects has the advantage of having a more consistent exposed interface; for example, + * if a consumer iterates over the comments on a Submission, all of the iterated items will actually be Comment objects, so the + * consumer won't encounter an unexpected `more` object at the end. However, there are a few disadvantages, namely that (a) this + * leads to an increase in internal complexity, and (b) there are a few cases where reddit's `more` objects have different amounts + * of available information (e.g. all the child IDs of a `more` object are known on creation), which leads to different optimal + * behavior. + */ +interface More extends Partial {} +class More { + ['constructor']: typeof More + static _name = 'More' + + children: string[] + _r!: snoowrap + + constructor ( + {children = [], ...options}: Partial = {}, + _r?: snoowrap, + public _hasFetched?: boolean + ) { + this.children = children + this.count = options.count + this.depth = options.depth + this.id = options.id + this.name = options.name + this.parent_id = options.parent_id + this.link_id = options.link_id + this._r = _r! + } + + /** + * Requests to /api/morechildren are capped at 20 comments at a time, but requests to /api/info are capped at 100, so + * it's easier to send to the latter. The disadvantage is that comment replies are not automatically sent from requests + * to /api/info. + */ + async fetchMore ( + options: FetchMoreOptions, + startIndex = 0, + children: {[id: string]: Comment} = {}, + nested = false + ): Promise> { + if (options.amount <= 0 || startIndex >= this.children.length) { + return new Listing({}, this._r) + } + if (!options.skipReplies) { + return this.fetchTree(options, startIndex, children, nested) + } + const ids = this.children.slice( + startIndex, + startIndex + Math.min(options.amount, MAX_API_INFO_AMOUNT) + ).map((id: String) => `t1_${id}`) + /** + * Requests are capped at 100 comments. Send lots of requests recursively to get the comments, then concatenate them. + * (This speed-requesting is only possible with comment Listings since the entire list of ids is present initially.) + */ + const response: Listing = await this._r._getListing({uri: 'api/info', qs: {id: ids.join(',')}}) + Object.assign(children, response._children) + const nexts = await this.fetchMore( + {...options, amount: options.amount - ids.length}, + startIndex + ids.length, + children, + true + ) + nexts.unshift(...response) + if (!nested) nexts._children = children + return nexts + } + + async fetchTree ( + options: FetchMoreOptions, + startIndex = 0, + children: {[id: string]: Comment} = {}, + nested = false + ): Promise> { + if (options.amount <= 0 || startIndex >= this.children.length) { + return new Listing({}, this._r) + } + const ids = this.children.slice( + startIndex, + startIndex + Math.min(options.amount, MAX_API_MORECHILDREN_AMOUNT) + ) + const response: JSONResponse = await this._r._get({ + url: 'api/morechildren', + params: {api_type: 'json', children: ids.join(','), link_id: this.link_id || this.parent_id} + }) + handleJsonErrors(response) + Object.assign(children, response._children) + const resultTrees: Comment[] = buildRepliesTree(response.json.data!.things) + /** + * Sometimes, when sending a request to reddit to get multiple comments from a `more` object, reddit decides to only + * send some of the requested comments, and then stub out the remaining ones in a smaller `more` object. ( ¯\_(ツ)_/¯ ) + * In these cases, recursively fetch the smaller `more` objects as well. + * + * (Maybe we should merge the smaller More objects into one big More object to save API requests?) + */ + const childMores: More[] = [] + const filteredComments: Comment[] = resultTrees.filter(child => { + if (child instanceof More) { + child.link_id = (this.link_id || this.parent_id)! + childMores.push(child) + return false + } + return true + }) + const moreComments: Comment[] = [] + for (const child of childMores) { + const expandedTree = await child.fetchTree({...options, amount: Infinity}, 0, children, true) + moreComments.push(...expandedTree) + } + const nexts = await this.fetchTree({...options, amount: options.amount - ids.length}, startIndex + ids.length, children, true) + nexts.unshift(...filteredComments, ...moreComments) + if (!nested) nexts._children = children + return nexts + } + + _clone () { + return new More( + { + children: [...this.children], + count: this.count, + depth: this.depth, + id: this.id, + name: this.name, + parent_id: this.parent_id, + link_id: this.link_id + }, + this._r + ) + } +} + +const emptyChildren = () => new More() + +export default More +export {Options, emptyChildren} diff --git a/src/objects/MultiReddit.d.ts b/src/objects/MultiReddit.d.ts index 88dca3c9..0d543da2 100644 --- a/src/objects/MultiReddit.d.ts +++ b/src/objects/MultiReddit.d.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import RedditUser from './RedditUser'; import RedditContent from './RedditContent'; import Subreddit from './Subreddit'; diff --git a/src/objects/MultiReddit.js b/src/objects/MultiReddit.js deleted file mode 100644 index 74c09102..00000000 --- a/src/objects/MultiReddit.js +++ /dev/null @@ -1,122 +0,0 @@ -import RedditContent from './RedditContent.js'; - -/** -* @summary A class representing a multireddit. -* -* @example -* -* // Get a multireddit belonging to a specific user -* r.getUser('multi-mod').getMultireddit('coding_languages') -*/ -const MultiReddit = class MultiReddit extends RedditContent { - constructor (options, _r, _hasFetched) { - super(options, _r, _hasFetched); - if (_hasFetched) { - this.curator = _r.getUser(this.path.split('/')[2]); - this.subreddits = this.subreddits.map(item => this._r._newObject('Subreddit', item.data || {display_name: item.name})); - } - } - get _uri () { - return `api/multi${this._path}?expand_srs=true`; - } - get _path () { - return `/user/${this.curator.name}/m/${this.name}`; - } - /** - * @summary Copies this multireddit to the requester's own account. - * @param {object} options - * @param {string} options.newName The new name for the copied multireddit - * @returns {Promise} A Promise for the newly-copied multireddit - * @example r.getUser('multi-mod').getMultireddit('coding_languages').copy({newName: 'my_coding_languages_copy'}) - */ - copy ({new_name, newName = new_name}) { - return this._r._getMyName().then(name => { - return this._post({uri: 'api/multi/copy', form: { - from: this._path, - to: `/user/${name}/m/${newName}`, - display_name: newName - }}); - }); - } - /** - * @summary Renames this multireddit. - * @desc **Note**: This method mutates this MultiReddit. - * @param {object} options - * @param {string} options.newName The new name for this multireddit. - * @returns {Promise} A Promise that fulfills with this multireddit - * @example r.getUser('multi-mod').getMultireddit('coding_languages').copy({newName: 'cookie_languages '}) - * @deprecated Reddit no longer provides the corresponding API endpoint. Please use `edit()` with a new name. - */ - rename ({new_name, newName = new_name}) { - return this._r._getMyName().then(name => this._post({ - uri: 'api/multi/rename', - form: {from: this._path, to: `/user/${name}/m/${newName}`, display_name: newName} - })).then(res => { - this.name = res.name; - }).return(this); - } - /** - * @summary Edits the properties of this multireddit. - * @desc **Note**: Any omitted properties here will simply retain their previous values. - * @param {object} options - * @param {string} [options.name] The name of the new multireddit. 50 characters max. - * @param {string} [options.description] A description for the new multireddit, in markdown. - * @param {string} [options.visibility] The multireddit's visibility setting. One of `private`, `public`, `hidden`. - * @param {string} [options.icon_name] One of `art and design`, `ask`, `books`, `business`, `cars`, `comics`, `cute animals`, - `diy`, `entertainment`, `food and drink`, `funny`, `games`, `grooming`, `health`, `life advice`, `military`, `models pinup`, - `music`, `news`, `philosophy`, `pictures and gifs`, `science`, `shopping`, `sports`, `style`, `tech`, `travel`, - `unusual stories`, `video`, `None` - * @param {string} [options.key_color] A six-digit RGB hex color, preceded by '#' - * @param {string} [options.weighting_scheme] One of 'classic', 'fresh' - * @returns {Promise} The updated version of this multireddit - * @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').edit({visibility: 'hidden'}) - */ - edit ({name = '', description, icon_name, key_color, visibility, weighting_scheme}) { - const display_name = name.length ? name : this.name; - return this._put({uri: `api/multi${this._path}`, form: {model: JSON.stringify({ - description_md: description, - display_name, - icon_name, - key_color, - visibility, - weighting_scheme - })}}); - } - /** - * @summary Adds a subreddit to this multireddit. - * @param {Subreddit} sub The Subreddit object to add (or a string representing a subreddit name) - * @returns {Promise} A Promise that fulfills with this multireddit when the reuqest is complete - * @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').addSubreddit('cookies') - */ - addSubreddit (sub) { - sub = typeof sub === 'string' ? sub : sub.display_name; - return this._put({uri: `api/multi${this._path}/r/${sub}`, form: {model: JSON.stringify({name: sub})}}).return(this); - } - /** - * @summary Removes a subreddit from this multireddit. - * @param {Subreddit} sub The Subreddit object to remove (or a string representing a subreddit name) - * @returns {Promise} A Promise that fulfills with this multireddit when the request is complete - * @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').removeSubreddit('cookies') - */ - removeSubreddit (sub) { - return this._delete({uri: `api/multi${this._path}/r/${typeof sub === 'string' ? sub : sub.display_name}`}).return(this); - } - /* Note: The endpoints GET/PUT /api/multi/multipath/description and GET /api/multi/multipath/r/srname are intentionally not - included, because they're redundant and the same thing can be achieved by simply using fetch() and edit(). */ -}; - -// MultiReddit#delete is not in the class body since Safari 9 can't parse the `delete` function name in class bodies. -/** -* @function -* @name delete -* @summary Deletes this multireddit. -* @returns {Promise} A Promise that fulfills when this request is complete -* @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').delete() -* @memberof MultiReddit -* @instance -*/ -Object.defineProperty(MultiReddit.prototype, 'delete', {value () { - return this._delete({uri: `api/multi${this._path}`}); -}, configurable: true, writable: true}); - -export default MultiReddit; diff --git a/src/objects/MultiReddit.ts b/src/objects/MultiReddit.ts new file mode 100644 index 00000000..f2499ddf --- /dev/null +++ b/src/objects/MultiReddit.ts @@ -0,0 +1,128 @@ +import RedditContent from './RedditContent'; +// +/** + * @summary A class representing a multireddit. + * + * @example + * + * // Get a multireddit belonging to a specific user + * r.getUser('multi-mod').getMultireddit('coding_languages') + */ +class MultiReddit extends RedditContent { + static _name = 'MultiReddit' + + constructor (options, _r, _hasFetched) { + super(options, _r, _hasFetched); + if (_hasFetched) { + this.curator = _r.getUser(this.path.split('/')[2]); + this.subreddits = this.subreddits.map(item => this._r._newObject('Subreddit', item.data || {display_name: item.name})); + } + } + get _uri () { + return `api/multi${this._path}?expand_srs=true`; + } + get _path () { + return `/user/${this.curator.name}/m/${this.name}`; + } + /** + * @summary Copies this multireddit to the requester's own account. + * @param {object} options + * @param {string} options.newName The new name for the copied multireddit + * @returns {Promise} A Promise for the newly-copied multireddit + * @example r.getUser('multi-mod').getMultireddit('coding_languages').copy({newName: 'my_coding_languages_copy'}) + */ + async copy ({new_name, newName = new_name}) { + const name = await this._r._getMyName(); + return this._post({url: 'api/multi/copy', form: { + from: this._path, + to: `/user/${name}/m/${newName}`, + display_name: newName + }}); + } + /** + * @summary Renames this multireddit. + * @desc **Note**: This method mutates this MultiReddit. + * @param {object} options + * @param {string} options.newName The new name for this multireddit. + * @returns {Promise} A Promise that fulfills with this multireddit + * @example r.getUser('multi-mod').getMultireddit('coding_languages').copy({newName: 'cookie_languages '}) + * @deprecated Reddit no longer provides the corresponding API endpoint. Please use `edit()` with a new name. + */ + async rename ({new_name, newName = new_name}) { + const name = await this._r._getMyName(); + const res = await this._post({ + url: 'api/multi/rename', + form: {from: this._path, to: `/user/${name}/m/${newName}`, display_name: newName} + }); + this.name = res.name; + return this; + } + /** + * @summary Edits the properties of this multireddit. + * @desc **Note**: Any omitted properties here will simply retain their previous values. + * @param {object} options + * @param {string} [options.name] The name of the new multireddit. 50 characters max. + * @param {string} [options.description] A description for the new multireddit, in markdown. + * @param {string} [options.visibility] The multireddit's visibility setting. One of `private`, `public`, `hidden`. + * @param {string} [options.icon_name] One of `art and design`, `ask`, `books`, `business`, `cars`, `comics`, `cute animals`, + * `diy`, `entertainment`, `food and drink`, `funny`, `games`, `grooming`, `health`, `life advice`, `military`, `models pinup`, + * `music`, `news`, `philosophy`, `pictures and gifs`, `science`, `shopping`, `sports`, `style`, `tech`, `travel`, + * `unusual stories`, `video`, `None` + * @param {string} [options.key_color] A six-digit RGB hex color, preceded by '#' + * @param {string} [options.weighting_scheme] One of 'classic', 'fresh' + * @returns {Promise} The updated version of this multireddit + * @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').edit({visibility: 'hidden'}) + */ + edit ({name = '', description, icon_name, key_color, visibility, weighting_scheme}) { + const display_name = name.length ? name : this.name; + return this._put({url: `api/multi${this._path}`, form: {model: JSON.stringify({ + description_md: description, + display_name, + icon_name, + key_color, + visibility, + weighting_scheme + })}}); + } + /** + * @summary Adds a subreddit to this multireddit. + * @param {Subreddit} sub The Subreddit object to add (or a string representing a subreddit name) + * @returns {Promise} A Promise that fulfills with this multireddit when the reuqest is complete + * @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').addSubreddit('cookies') + */ + async addSubreddit (sub) { + sub = typeof sub === 'string' ? sub : sub.display_name; + await this._put({url: `api/multi${this._path}/r/${sub}`, form: {model: JSON.stringify({name: sub})}}); + return this; + } + /** + * @summary Removes a subreddit from this multireddit. + * @param {Subreddit} sub The Subreddit object to remove (or a string representing a subreddit name) + * @returns {Promise} A Promise that fulfills with this multireddit when the request is complete + * @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').removeSubreddit('cookies') + */ + async removeSubreddit (sub) { + await this._delete({url: `api/multi${this._path}/r/${typeof sub === 'string' ? sub : sub.display_name}`}); + return this; + } + /** + * Note: The endpoints GET/PUT /api/multi/multipath/description and GET /api/multi/multipath/r/srname are intentionally not + * included, because they're redundant and the same thing can be achieved by simply using fetch() and edit(). + */ +}; + +// MultiReddit#delete is not in the class body since Safari 9 can't parse the `delete` function name in class bodies. +/** + * @function + * @name delete + * @summary Deletes this multireddit. + * @returns {Promise} A Promise that fulfills when this request is complete + * @example r.getUser('not_an_aardvark').getMultireddit('cookie_languages').delete() + * @memberof MultiReddit + * @instance + */ +Object.defineProperty(MultiReddit.prototype, 'delete', {value () { + return this._delete({url: `api/multi${this._path}`}); +}, configurable: true, writable: true}); + +export default MultiReddit; diff --git a/src/objects/PrivateMessage.d.ts b/src/objects/PrivateMessage.d.ts deleted file mode 100644 index 0150bbec..00000000 --- a/src/objects/PrivateMessage.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Listing from './Listing'; -import RedditUser from './RedditUser'; -import ReplyableContent from './ReplyableContent'; -import Subreddit from './Subreddit'; - -export default class PrivateMessage extends ReplyableContent { - author: RedditUser; - body_html: string; - body: string; - context: string; - dest: string; - distinguished: string | null; - first_message_name: string; - first_message: number; - likes: any; // ? - new: boolean; - num_comments: number; - parent_id: string; - replies: Listing; - score: number; - subject: string; - subreddit_name_prefixed: string; - subreddit: Subreddit; - was_comment: boolean; - - deleteFromInbox(): Promise; - markAsRead(): Promise; - markAsUnread(): Promise; - muteAuthor(): Promise; - unmuteAuthor(): Promise; -} diff --git a/src/objects/PrivateMessage.js b/src/objects/PrivateMessage.js deleted file mode 100644 index 5d56b560..00000000 --- a/src/objects/PrivateMessage.js +++ /dev/null @@ -1,68 +0,0 @@ -import {buildRepliesTree, findMessageInTree} from '../helpers.js'; -import ReplyableContent from './ReplyableContent.js'; - -/** -* A class representing a private message or a modmail. -* -* @example -* -* // Get a Private Message with a given ID -* r.getMessage('51shnw') -* @extends ReplyableContent -*/ -const PrivateMessage = class PrivateMessage extends ReplyableContent { - get _uri () { - return `message/messages/${this.name.slice(3)}`; - } - _transformApiResponse (response) { - response[0].replies = buildRepliesTree(response[0].replies || []); - return findMessageInTree(this.name, response[0]); - } - // TODO: Get rid of the repeated code here, most of these methods are exactly the same with the exception of the URIs - /** - * @summary Marks this message as read. - * @returns {Promise} A Promise that fulfills with this message after the request is complete - * @example r.getMessage('51shxv').markAsRead() - */ - markAsRead () { - return this._r.markMessagesAsRead([this]).return(this); - } - /** - * @summary Marks this message as unread. - * @returns {Promise} A Promise that fulfills with this message after the request is complete - * @example r.getMessage('51shxv').markAsUnread() - */ - markAsUnread () { - return this._r.markMessagesAsUnread([this]).return(this); - } - /** - * @summary Mutes the author of this message for 72 hours. This can only be used on moderator mail. - * @returns {Promise} A Promise that fulfills with this message after the request is complete - * @example r.getMessage('51shxv').muteAuthor() - */ - muteAuthor () { - return this._post({uri: 'api/mute_message_author', form: {id: this.name}}).return(this); - } - /** - * @summary Unmutes the author of this message. - * @returns {Promise} A Promise that fulfills with this message after the request is complete - * @example r.getMessage('51shxv').unmuteAuthor() - */ - unmuteAuthor () { - return this._post({uri: 'api/unmute_message_author', form: {id: this.name}}).return(this); - } - /** - * @summary Deletes this message from the authenticated user's inbox. - * @desc This only removes the item from the authenticated user's inbox. It has no effect on how the item looks to the sender. - * @returns {Promise} A Promise that fulfills with this message when the request is complete. - * @example - * - * const firstMessage = r.getInbox().get(0); - * firstMessage.deleteFromInbox(); - */ - deleteFromInbox () { - return this._post({uri: 'api/del_msg', form: {id: this.name}}).return(this); - } -}; - -export default PrivateMessage; diff --git a/src/objects/PrivateMessage.ts b/src/objects/PrivateMessage.ts new file mode 100644 index 00000000..80f5f8bc --- /dev/null +++ b/src/objects/PrivateMessage.ts @@ -0,0 +1,98 @@ +import {buildRepliesTree, findMessageInTree} from '../helper' +import ReplyableContent from './ReplyableContent' +import type Listing from './Listing' +import type RedditUser from './RedditUser' +import type Subreddit from './Subreddit' +// + +interface PrivateMessage { + author: RedditUser + body_html: string + body: string + context: string + dest: string + distinguished: string | null + first_message_name: string + first_message: number + likes: any // ? + new: boolean + num_comments: number + parent_id: string + replies: Listing + score: number + subject: string + subreddit_name_prefixed: string + subreddit: Subreddit + was_comment: boolean +} + +/** + * A class representing a private message or a modmail. + * @example + * + * // Get a Private Message with a given ID + * r.getMessage('51shnw') + */ +class PrivateMessage extends ReplyableContent { + static _name = 'PrivateMessage' + + get _uri () { + return `message/messages/${this.name.slice(3)}` + } + _transformApiResponse (response: any) { + response[0].replies = buildRepliesTree(response[0].replies || []) + return findMessageInTree(this.name, response[0]) + } + // TODO: Get rid of the repeated code here, most of these methods are exactly the same with the exception of the URIs + /** + * @summary Marks this message as read. + * @returns {Promise} A Promise that fulfills with this message after the request is complete + * @example r.getMessage('51shxv').markAsRead() + */ + async markAsRead () { + await this._r.markMessagesAsRead([this]) + return this + } + /** + * @summary Marks this message as unread. + * @returns {Promise} A Promise that fulfills with this message after the request is complete + * @example r.getMessage('51shxv').markAsUnread() + */ + async markAsUnread () { + await this._r.markMessagesAsUnread([this]) + return this + } + /** + * @summary Mutes the author of this message for 72 hours. This can only be used on moderator mail. + * @returns {Promise} A Promise that fulfills with this message after the request is complete + * @example r.getMessage('51shxv').muteAuthor() + */ + async muteAuthor () { + await this._post({url: 'api/mute_message_author', form: {id: this.name}}) + return this + } + /** + * @summary Unmutes the author of this message. + * @returns {Promise} A Promise that fulfills with this message after the request is complete + * @example r.getMessage('51shxv').unmuteAuthor() + */ + async unmuteAuthor () { + await this._post({url: 'api/unmute_message_author', form: {id: this.name}}) + return this + } + /** + * @summary Deletes this message from the authenticated user's inbox. + * @desc This only removes the item from the authenticated user's inbox. It has no effect on how the item looks to the sender. + * @returns {Promise} A Promise that fulfills with this message when the request is complete. + * @example + * + * const firstMessage = r.getInbox().get(0); + * firstMessage.deleteFromInbox(); + */ + async deleteFromInbox () { + await this._post({url: 'api/del_msg', form: {id: this.name}}) + return this + } +} + +export default PrivateMessage diff --git a/src/objects/RedditContent.d.ts b/src/objects/RedditContent.d.ts deleted file mode 100644 index fb18c670..00000000 --- a/src/objects/RedditContent.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as Snoowrap from '../snoowrap'; - -export default class RedditContent extends Promise { - created_utc: number; - created: number; - id: string; - name: string; - protected _r: Snoowrap; - protected _fetch?: boolean; - protected _hasFetched: boolean; - - constructor(options: any, _r: Snoowrap, _hasFetched: boolean); - fetch(): Promise; - refresh(): Promise; - toJSON(): T; -} diff --git a/src/objects/RedditContent.js b/src/objects/RedditContent.js deleted file mode 100644 index b0584e49..00000000 --- a/src/objects/RedditContent.js +++ /dev/null @@ -1,131 +0,0 @@ -import {cloneDeep, mapValues, pick} from 'lodash'; -import Promise from '../Promise.js'; -import util from 'util'; -import {defineInspectFunc} from '../helpers.js'; -import {HTTP_VERBS, USER_KEYS, SUBREDDIT_KEYS} from '../constants.js'; -import Listing from './Listing.js'; - -/** -* A base class for content from reddit. With the expection of Listings, all content types extend this class. -* This class should be considered 'abstract', to the extend that JavaScript classes can be -- it should not be necessary to -* instantiate it directly. -* -*/ -const RedditContent = class RedditContent { - constructor (options, _r, _hasFetched) { - // _r refers to the snoowrap requester that is used to fetch this content. - this._r = _r; - this._fetch = null; - this._hasFetched = !!_hasFetched; - Object.assign(this, options); - if (typeof Proxy !== 'undefined' && !this._hasFetched && _r._config.proxies) { - return new Proxy(this, {get (target, key) { - return key in target || key === 'length' || key in Promise.prototype ? target[key] : target.fetch()[key]; - }}); - } - } - /** - * @summary Fetches this content from reddit. - * @desc This will not mutate the original content object; all Promise properties will remain as Promises after the content has - * been fetched. However, the information on this object will be cached, so it may become out-of-date with the content on - * reddit. To clear the cache and fetch this object from reddit again, use `refresh()`. - * - * If snoowrap is running in an environment that supports ES2015 Proxies (e.g. Chrome 49+), then `fetch()` will get - * automatically called when an unknown property is accessed on an unfetched content object. - * @returns {Promise} A version of this object with all of its fetched properties from reddit. This will not mutate the - object. Once an object has been fetched once, its properties will be cached, so they might end up out-of-date if this - function is called again. To refresh an object, use refresh(). - * @example - * - * r.getUser('not_an_aardvark').fetch().then(userInfo => { - * console.log(userInfo.name); // 'not_an_aardvark' - * console.log(userInfo.created_utc); // 1419104352 - * }); - * - * r.getComment('d1xchqn').fetch().then(comment => comment.body).then(console.log) - * // => 'This is a little too interesting for my liking' - * - * // In environments that support ES2015 Proxies, the above line is equivalent to: - * r.getComment('d1xchqn').body.then(console.log); - * // => 'This is a little too interesting for my liking' - */ - fetch () { - if (!this._fetch) { - this._fetch = this._r._promiseWrap(this._r._get({uri: this._uri}).then(res => this._transformApiResponse(res))); - } - return this._fetch; - } - /** - * @summary Refreshes this content. - * @returns {Promise} A newly-fetched version of this content - * @example - * - * var someComment = r.getComment('cmfkyus'); - * var initialCommentBody = some_comment.fetch().then(comment => comment.body); - * - * setTimeout(() => { - * someComment.refresh().then(refreshedComment => { - * if (initialCommentBody.value() !== refreshedComment.body) { - * console.log('This comment has changed since 10 seconds ago.'); - * } - * }); - * }, 10000); - */ - refresh () { - this._fetch = null; - return this.fetch(); - } - /** - * @summary Returns a stringifyable version of this object. - * @desc It is usually not necessary to call this method directly; simply running JSON.stringify(some_object) will strip the - private properties anyway. - * @returns {object} A version of this object with all the private properties stripped - * @example - * - * var user = r.getUser('not_an_aardvark'); - * JSON.stringify(user) // => '{"name":"not_an_aardvark"}' - */ - toJSON () { - return mapValues(this._stripPrivateProps(), (value, key) => { - if (value instanceof RedditContent && !value._hasFetched) { - if (value.constructor._name === 'RedditUser' && USER_KEYS.has(key)) { - return value.name; - } - if (value.constructor._name === 'Subreddit' && SUBREDDIT_KEYS.has(key)) { - return value.display_name; - } - } - return value && value.toJSON ? value.toJSON() : value; - }); - } - _stripPrivateProps () { - return pick(this, Object.keys(this).filter(key => !key.startsWith('_'))); - } - _transformApiResponse (response) { - return response; - } - _clone ({deep = false} = {}) { - const clonedProps = mapValues(this, value => { - if (deep) { - return value instanceof RedditContent || value instanceof Listing ? value._clone({deep}) : cloneDeep(value); - } - return value; - }); - return this._r._newObject(this.constructor._name, clonedProps, this._hasFetched); - } - _getListing (...args) { - return this._r._getListing(...args); - } -}; - -defineInspectFunc(RedditContent.prototype, function () { - return `${this.constructor._name} ${util.inspect(this._stripPrivateProps())}`; -}); - -HTTP_VERBS.forEach(method => { - Object.defineProperty(RedditContent.prototype, `_${method}`, {value (...args) { - return this._r[`_${method}`](...args); - }, configurable: true, writable: true}); -}); - -export default RedditContent; diff --git a/src/objects/RedditContent.ts b/src/objects/RedditContent.ts new file mode 100644 index 00000000..4a6b16f7 --- /dev/null +++ b/src/objects/RedditContent.ts @@ -0,0 +1,181 @@ +import util from 'util' +import {defineInspectFunc} from '../helper' +import {USER_KEYS, SUBREDDIT_KEYS} from '../constants' +import {isBrowser} from '../helpers' +import type snoowrap from '../snoowrap' +import type {Children} from '../interfaces' + +/** + * A base class for content from reddit. With the expection of Listings, all content types extend this class. + * This class should be considered 'abstract', to the extend that JavaScript classes can be -- it should not be necessary to + * instantiate it directly. + * + */ +class RedditContent> { + ['constructor']: typeof RedditContent + static _name = 'RedditContent' + + _r: snoowrap + _hasFetched: boolean + _fetch?: T + __uri?: string + get _uri () { + return this.__uri + } + set _uri (uri) { + this.__uri = uri + } + /* + created_utc!: number + created!: number + id!: string + name!: string + */ + [key: string]: any + + constructor (options: {[key: string]: any}, _r: snoowrap, _hasFetched = false) { + // _r refers to the snoowrap requester that is used to fetch this content. + this._r = _r + this._hasFetched = _hasFetched + Object.assign(this, options) + } + + /** + * @summary Fetches this content from reddit. + * @desc This will not mutate the original content object; all Promise properties will remain as Promises after the content has + * been fetched. However, the information on this object will be cached, so it may become out-of-date with the content on + * reddit. To clear the cache and fetch this object from reddit again, use `refresh()`. + * + * If snoowrap is running in an environment that supports ES2015 Proxies (e.g. Chrome 49+), then `fetch()` will get + * automatically called when an unknown property is accessed on an unfetched content object. + * @returns A version of this object with all of its fetched properties from reddit. This will not mutate the + * object. Once an object has been fetched once, its properties will be cached, so they might end up out-of-date if this + * function is called again. To refresh an object, use refresh(). + * @example + * + * r.getUser('not_an_aardvark').fetch().then(userInfo => { + * console.log(userInfo.name); // 'not_an_aardvark' + * console.log(userInfo.created_utc); // 1419104352 + * }); + * + * r.getComment('d1xchqn').fetch().then(comment => comment.body).then(console.log) + * // => 'This is a little too interesting for my liking' + * + * // In environments that support ES2015 Proxies, the above line is equivalent to: + * r.getComment('d1xchqn').body.then(console.log); + * // => 'This is a little too interesting for my liking' + */ + async fetch () { + if (!this._fetch) { + let res = await this._r._get({url: this._uri}) + res = this._transformApiResponse(res) + this._fetch = res + } + return this._fetch + } + + /** + * @summary Refreshes this content. + * @returns A newly-fetched version of this content + * @example + * + * var someComment = r.getComment('cmfkyus'); + * var initialCommentBody = some_comment.fetch().then(comment => comment.body); + * + * setTimeout(() => { + * someComment.refresh().then(refreshedComment => { + * if (initialCommentBody.value() !== refreshedComment.body) { + * console.log('This comment has changed since 10 seconds ago.'); + * } + * }); + * }, 10000); + */ + refresh () { + this._fetch = undefined + return this.fetch() + } + + _clone (deep = false, _children: Children = {}): T { + const clonedProps = this._cloneProps(deep, _children) + const name = this.constructor._name as keyof typeof snoowrap.objects + return this._r._newObject(name, clonedProps, this._hasFetched) + } + + _cloneProps (deep = false, _children: Children = {}) { + const clonedProps: {[key: string]: any} = {} + for (const key of Object.keys(this)) { + let value = this[key] + if (deep) { + value = value instanceof RedditContent || value.constructor._name === 'Listing' + ? value._clone(deep, _children) + : typeof value === 'object' && value !== null + ? this._cloneProps.call(value, deep, _children) + : value + } + if (value.constructor._name === 'Comment') { + _children[value.id] = value + } + clonedProps[key] = value + } + return clonedProps + } + + /** + * @summary Returns a stringifyable version of this object. + * @desc It is usually not necessary to call this method directly; simply running JSON.stringify(some_object) will strip the + * private properties anyway. + * @returns {object} A version of this object with all the private properties stripped + * @example + * + * var user = r.getUser('not_an_aardvark'); + * JSON.stringify(user) // => '{"name":"not_an_aardvark"}' + */ + toJSON () { + const object: {[key: string]: any} = {} + for (const key of Object.keys(this)) { + if (key.startsWith('_')) continue + let value = this[key] + if (value instanceof RedditContent && !value._hasFetched) { + if (value.constructor._name === 'RedditUser' && USER_KEYS.has(key)) value = value.name + if (value.constructor._name === 'Subreddit' && SUBREDDIT_KEYS.has(key)) value = value.display_name + } + object[key] = value && value.toJSON ? value.toJSON() : value + } + return object + } + + _transformApiResponse (response: any) { + return response + } + + get _getListing () { + return this._r._getListing + } + + get _get () { + return this._r._get + } + get _post () { + return this._r._post + } + get _put () { + return this._r._put + } + get _delete () { + return this._r._delete + } + get _head () { + return this._r._head + } + get _patch () { + return this._r._patch + } +} + +if (!isBrowser) { + defineInspectFunc(RedditContent.prototype, function (this: RedditContent) { // Fake param + return `${this.constructor._name} ${util.inspect(this)}` + }) +} + +export default RedditContent diff --git a/src/objects/RedditUser.js b/src/objects/RedditUser.js deleted file mode 100644 index 2334187e..00000000 --- a/src/objects/RedditUser.js +++ /dev/null @@ -1,272 +0,0 @@ -import {USERNAME_REGEX} from '../constants.js'; -import {InvalidMethodCallError, InvalidUserError} from '../errors.js'; -import RedditContent from './RedditContent.js'; - -/** -* A class representing a reddit user -* -* @extends ReplyableContent -* @example -* -* // Get a user with the given username -* r.getUser('spez') -*/ -const RedditUser = class RedditUser extends RedditContent { - get _uri () { - if (typeof this.name !== 'string' || !USERNAME_REGEX.test(this.name)) { - throw new InvalidUserError(this.name); - } - return `user/${this.name}/about`; - } - /** - * @summary Gives reddit gold to a user - * @param {number} months The number of months of gold to give. This must be a number between 1 and 36. - * @returns {Promise} A Promise that fulfills when the request is complete - * @example r.getUser('not_an_aardvark').giveGold(12) - */ - giveGold (months) { - /* Ideally this would allow for more than 36 months by sending multiple requests, but I don't have the resources to test - that code, and it's probably better that such a big investment be deliberate anyway. */ - if (typeof months !== 'number' || months < 1 || months > 36) { - throw new InvalidMethodCallError('Invalid argument to RedditUser#giveGold; `months` must be between 1 and 36.'); - } - return this._post({uri: `api/v1/gold/give/${this.name}`, form: {months}}); - } - /** - * Assigns flair to this user on a given subreddit (as a moderator). - * @param {object} options - * @param {string} options.subredditName The subreddit that flair should be assigned on - * @param {string} [options.text=''] The text that the user's flair should have - * @param {string} [options.cssClass=''] The CSS class that the user's flair should have - * @returns {Promise} A Promise that fulfills with the current user after the request is complete - * @example r.getUser('not_an_aardvark').assignFlair({subredditName: 'snoowrap', text: "Isn't an aardvark"}) - */ - assignFlair (options) { - return this._r._assignFlair({...options, name: this.name}).return(this); - } - /** - * @summary Adds this user as a friend, or modifies their friend note. - * @desc **Note:** reddit.com only permits "notes" to be added on friends if the authenticated account has a subscription to - reddit gold. - * @param {object} options - * @param {string} [options.note] An optional note to add on the user (300 characters max) - * @returns {Promise} A Promise that fulfills when this request is complete - * @example r.getUser('actually_an_aardvark').friend({note: 'Is an aardvark'}) - */ - friend ({note} = {}) { - return this._put({uri: `api/v1/me/friends/${this.name}`, body: {user: this.name, note}}).return(this); - } - /** - * @summary Removes this user from the requester's friend list. - * @returns {Promise} A Promise that fulfills with this user when the request is complete - * @example r.getUser('actually_an_aardvark').unfriend() - */ - unfriend () { - return this._delete({uri: `api/v1/me/friends/${this.name}`}); - } - /** - * @summary Gets information on this user related to their presence on the friend list. - * @returns {Promise} A Promise that fulfills with an object containing friend information - * @example - * - * r.getUser('not_an_aardvark').getFriendInformation().then(console.log) - * // => { date: 1460318190, note: 'Is an aardvark', name: 'actually_an_aardvark', id: 't2_q3519' } - */ - getFriendInformation () { - return this._get({uri: `api/v1/me/friends/${this.name}`}); - } - /** - * @summary Gets a list of this user's trophies. - * @returns {Promise} A TrophyList containing this user's trophies - * @example - * - * r.getUser('not_an_aardvark').getTrophies().then(console.log) - * // => TrophyList { trophies: [ - * // Trophy { ... }, - * // Trophy { ... }, - * // ... - * // ] } - */ - getTrophies () { - return this._get({uri: `api/v1/user/${this.name}/trophies`}); - } - /** - * @summary Gets a Listing of the content this user has submitted. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions and Comments - * @example - * - * r.getUser('spez').getOverview().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getOverview (options) { - return this._getListing({uri: `user/${this.name}/overview`, qs: options}); - } - /** - * @summary Gets a Listing of this user's submissions. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions - * @example - * - * r.getUser('spez').getSubmissions().then(console.log) - * // => Listing [ - * // Submission { ... }, - * // Submission { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getSubmissions (options) { - return this._getListing({uri: `user/${this.name}/submitted`, qs: options}); - } - /** - * @summary Gets a Listing of this user's comments. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Comments - * @example - * - * r.getUser('spez').getComments().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Comment { ... }, - * // ... - * // ] - */ - getComments (options) { - return this._getListing({uri: `user/${this.name}/comments`, qs: options}); - } - /** - * @summary Gets a Listing of the content that this user has upvoted. - * @desc **Note**: This can only be used to view one's own upvoted content, unless the user in question has chosen to - make this information public in their preferences. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions and Comments - * @example - * - * r.getMe().getUpvotedContent().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getUpvotedContent (options) { - return this._getListing({uri: `user/${this.name}/upvoted`, qs: options}); - } - /** - * @summary Gets a Listing of the content that this user has downvoted. - * @desc **Note**: This can only be used to view one's own downvoted content, unless the user in question has chosen to - make this information public in their preferences. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions and Comments - * @example - * - * r.getMe().getDownvotedContent().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getDownvotedContent (options) { - return this._getListing({uri: `user/${this.name}/downvoted`, qs: options}); - } - /** - * @summary Gets a Listing of the submissions that this user has hidden. - * @desc **Note**: This can only be used to view one's own set of hidden posts, as reddit will return a 403 error when - attempting to view another users' hidden posts. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions - * @example - * - * r.getMe().getHiddenContent().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getHiddenContent (options) { - return this._getListing({uri: `user/${this.name}/hidden`, qs: options}); - } - /** - * @summary Gets a Listing of the content that this user has saved. - * @desc **Note**: This can only be used to view one's own set of saved content, as reddit will return a 403 error when - attempting to view other users' saved content. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions and Comments. - * @example - * - * r.getMe().getSavedContent().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getSavedContent (options) { - return this._getListing({uri: `user/${this.name}/saved`, qs: options}); - } - /** - * @summary Gets a Listing of this user's content which has been gilded. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing Submissions and Comments - * @example - * - * r.getMe().getGildedContent().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getGildedContent (options) { - return this._getListing({uri: `user/${this.name}/gilded`, qs: options}); - } - /** - * @summary Gets a multireddit belonging to this user. - * @param {string} name The name of the multireddit - * @returns {MultiReddit} An unfetched MultiReddit object - * @example - * - * r.getUser('multi-mod').getMultireddit('coding_languages') - * // => MultiReddit { - * // name: 'coding_languages', - * // curator: RedditUser { name: 'multi-mod' }, - * // path: '/user/multi-mod/m/coding_languages' - * // } - */ - getMultireddit (name) { - return this._r._newObject('MultiReddit', {name, curator: this}); - } - /** - * @summary Gets an Array of all of this user's MultiReddits. - * @returns {Promise} A Promise that fulfills with an Array containing MultiReddits. - * @example - * - * r.getUser('multi-mod').getMultireddits().then(console.log) - * - * // => [ - * MultiReddit { ... }, - * MultiReddit { ... }, - * MultiReddit { ... }, - * ... - * ] - */ - getMultireddits () { - return this._get({uri: `api/multi/user/${this.name}`, qs: {expand_srs: true}}); - } -}; - -export default RedditUser; diff --git a/src/objects/RedditUser.ts b/src/objects/RedditUser.ts new file mode 100644 index 00000000..d6788536 --- /dev/null +++ b/src/objects/RedditUser.ts @@ -0,0 +1,291 @@ +import {USERNAME_REGEX} from '../constants' +import {InvalidMethodCallError, InvalidUserError} from '../errors' +import RedditContent from './RedditContent' +import type {AssignFlairOptions, OmitProps} from '../interfaces' +import type {ListingQuery} from './Listing' + + +/** + * A class representing a reddit user + * @example + * + * // Get a user with the given username + * r.getUser('spez') + */ +class RedditUser extends RedditContent { + static _name = 'RedditUser' + + get _uri () { + if (typeof this.name !== 'string' || !USERNAME_REGEX.test(this.name)) { + throw new InvalidUserError(this.name) + } + return `user/${this.name}/about` + } + + /** + * @summary Gives reddit gold to a user + * @param months The number of months of gold to give. This must be a number between 1 and 36. + * @returns A Promise that fulfills when the request is complete + * @example r.getUser('not_an_aardvark').giveGold(12) + */ + giveGold (months: number) { + /** + * Ideally this would allow for more than 36 months by sending multiple requests, but I don't have the resources to test + * that code, and it's probably better that such a big investment be deliberate anyway. + */ + if (typeof months !== 'number' || months < 1 || months > 36) { + throw new InvalidMethodCallError('Invalid argument to RedditUser#giveGold; `months` must be between 1 and 36.') + } + return this._post({url: `api/v1/gold/give/${this.name}`, form: {months: months + ''}}) + } + + /** + * Assigns flair to this user on a given subreddit (as a moderator). + * @param {object} options + * @returns A Promise that fulfills with the current user after the request is complete + * @example r.getUser('not_an_aardvark').assignFlair({subredditName: 'snoowrap', text: "Isn't an aardvark"}) + */ + async assignFlair (options: OmitProps) { + await this._r._assignFlair({...options, name: this.name}) + return this + } + + /** + * @summary Adds this user as a friend, or modifies their friend note. + * @desc **Note:** reddit.com only permits "notes" to be added on friends if the authenticated account has a subscription to + * reddit gold. + * @param note An optional note to add on the user (300 characters max) + * @returns A Promise that fulfills when this request is complete + * @example r.getUser('actually_an_aardvark').friend({note: 'Is an aardvark'}) + */ + async friend (note?: string) { + await this._put({url: `api/v1/me/friends/${this.name}`, data: {user: this.name, note}}) + return this + } + + /** + * @summary Removes this user from the requester's friend list. + * @returns A Promise that fulfills with this user when the request is complete + * @example r.getUser('actually_an_aardvark').unfriend() + */ + unfriend () { + return this._delete({url: `api/v1/me/friends/${this.name}`}) + } + + /** + * @summary Gets information on this user related to their presence on the friend list. + * @returns A Promise that fulfills with an object containing friend information + * @example + * + * r.getUser('not_an_aardvark').getFriendInformation().then(console.log) + * // => { date: 1460318190, note: 'Is an aardvark', name: 'actually_an_aardvark', id: 't2_q3519' } + */ + getFriendInformation () { + return this._get({url: `api/v1/me/friends/${this.name}`}) + } + + /** + * @summary Gets a list of this user's trophies. + * @returns A TrophyList containing this user's trophies + * @example + * + * r.getUser('not_an_aardvark').getTrophies().then(console.log) + * // => TrophyList { trophies: [ + * // Trophy { ... }, + * // Trophy { ... }, + * // ... + * // ] } + */ + getTrophies () { + return this._get({url: `api/v1/user/${this.name}/trophies`}) + } + + /** + * @summary Gets a Listing of the content this user has submitted. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Submissions and Comments + * @example + * + * r.getUser('spez').getOverview().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getOverview (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/overview`, qs: options}) + } + + /** + * @summary Gets a Listing of this user's submissions. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Submissions + * @example + * + * r.getUser('spez').getSubmissions().then(console.log) + * // => Listing [ + * // Submission { ... }, + * // Submission { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getSubmissions (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/submitted`, qs: options}) + } + + /** + * @summary Gets a Listing of this user's comments. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Comments + * @example + * + * r.getUser('spez').getComments().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Comment { ... }, + * // ... + * // ] + */ + getComments (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/comments`, qs: options}) + } + + /** + * @summary Gets a Listing of the content that this user has upvoted. + * @desc **Note**: This can only be used to view one's own upvoted content, unless the user in question has chosen to + * make this information public in their preferences. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Submissions and Comments + * @example + * + * r.getMe().getUpvotedContent().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getUpvotedContent (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/upvoted`, qs: options}) + } + + /** + * @summary Gets a Listing of the content that this user has downvoted. + * @desc **Note**: This can only be used to view one's own downvoted content, unless the user in question has chosen to + * make this information public in their preferences. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Submissions and Comments + * @example + * + * r.getMe().getDownvotedContent().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getDownvotedContent (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/downvoted`, qs: options}) + } + + /** + * @summary Gets a Listing of the submissions that this user has hidden. + * @desc **Note**: This can only be used to view one's own set of hidden posts, as reddit will return a 403 error when + * attempting to view another users' hidden posts. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Submissions + * @example + * + * r.getMe().getHiddenContent().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getHiddenContent (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/hidden`, qs: options}) + } + + /** + * @summary Gets a Listing of the content that this user has saved. + * @desc **Note**: This can only be used to view one's own set of saved content, as reddit will return a 403 error when + * attempting to view other users' saved content. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Submissions and Comments. + * @example + * + * r.getMe().getSavedContent().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getSavedContent (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/saved`, qs: options}) + } + + /** + * @summary Gets a Listing of this user's content which has been gilded. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Submissions and Comments + * @example + * + * r.getMe().getGildedContent().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getGildedContent (options: ListingQuery) { + return this._getListing({uri: `user/${this.name}/gilded`, qs: options}) + } + + /** + * @summary Gets a multireddit belonging to this user. + * @param {string} name The name of the multireddit + * @returns {MultiReddit} An unfetched MultiReddit object + * @example + * + * r.getUser('multi-mod').getMultireddit('coding_languages') + * // => MultiReddit { + * // name: 'coding_languages', + * // curator: RedditUser { name: 'multi-mod' }, + * // path: '/user/multi-mod/m/coding_languages' + * // } + */ + getMultireddit (name: string) { + return this._r._newObject('MultiReddit', {name, curator: this}) + } + + /** + * @summary Gets an Array of all of this user's MultiReddits. + * @returns A Promise that fulfills with an Array containing MultiReddits. + * @example + * + * r.getUser('multi-mod').getMultireddits().then(console.log) + * + * // => [ + * MultiReddit { ... }, + * MultiReddit { ... }, + * MultiReddit { ... }, + * ... + * ] + */ + getMultireddits () { + return this._get({url: `api/multi/user/${this.name}`, params: {expand_srs: true}}) + } +} + +export default RedditUser diff --git a/src/objects/ReplyableContent.d.ts b/src/objects/ReplyableContent.d.ts deleted file mode 100644 index 88b9d36e..00000000 --- a/src/objects/ReplyableContent.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import RedditContent from './RedditContent'; - -export default class ReplyableContent extends RedditContent { - approve(): Promise; - blockAuthor(): Promise; - ignoreReports(): Promise; - remove(options?: { spam?: boolean }): Promise; - reply(text: string): Promise>; - report(options?: { reason?: string }): Promise; - unignoreReports(): Promise; -} diff --git a/src/objects/ReplyableContent.js b/src/objects/ReplyableContent.js deleted file mode 100644 index ce4d4575..00000000 --- a/src/objects/ReplyableContent.js +++ /dev/null @@ -1,90 +0,0 @@ -import {handleJsonErrors} from '../helpers.js'; -import RedditContent from './RedditContent.js'; - -const api_type = 'json'; - -/** -* A set of mixin functions that apply to Submissions, Comments, and PrivateMessages -* -* @extends RedditContent -*/ -const ReplyableContent = class ReplyableContent extends RedditContent { - /** - * @summary Removes this Comment, Submission or PrivateMessage from public listings. - * @desc This requires the authenticated user to be a moderator of the subreddit with the `posts` permission. - * @param {object} options - * @param {boolean} [options.spam=false] Determines whether this should be marked as spam - * @returns {Promise} A Promise that fulfills with this content when the request is complete - * @example r.getComment('c08pp5z').remove({spam: true}) - */ - remove ({spam = false} = {}) { - return this._post({uri: 'api/remove', form: {spam, id: this.name}}).return(this); - } - /** - * @summary Approves this Comment, Submission, or PrivateMessage, re-adding it to public listings if it had been removed - * @returns {Promise} A Promise that fulfills with this content when the request is complete - * @example r.getComment('c08pp5z').approve() - */ - approve () { - return this._post({uri: 'api/approve', form: {id: this.name}}).return(this); - } - /** - * @summary Reports this content anonymously to subreddit moderators (for Comments and Submissions) - or to the reddit admins (for PrivateMessages) - * @param {object} [options] - * @param {string} [options.reason] The reason for the report - * @returns {Promise} A Promise that fulfills with this content when the request is complete - * @example r.getComment('c08pp5z').report({reason: 'Breaking the subreddit rules'}) - */ - report ({reason} = {}) { - return this._post({uri: 'api/report', form: { - api_type, reason: 'other', other_reason: reason, thing_id: this.name - }}).return(this); - } - /** - * @summary Ignores reports on this Comment, Submission, or PrivateMessage - * @returns {Promise} A Promise that fulfills with this content when the request is complete - * @example r.getComment('c08pp5z').ignoreReports() - */ - ignoreReports () { - return this._post({uri: 'api/ignore_reports', form: {id: this.name}}).return(this); - } - /** - * @summary Unignores reports on this Comment, Submission, or PrivateMessages - * @returns {Promise} A Promise that fulfills with this content when the request is complete - * @example r.getComment('c08pp5z').unignoreReports() - */ - unignoreReports () { - return this._post({uri: 'api/unignore_reports', form: {id: this.name}}).return(this); - } - /** - * @summary Submits a new reply to this object. (This takes the form of a new Comment if this object is a Submission/Comment, - or a new PrivateMessage if this object is a PrivateMessage.) - * @param {string} text The content of the reply, in raw markdown text - * @returns {Promise} A Promise that fulfills with the newly-created reply - * @example r.getSubmission('4e60m3').reply('This was an interesting post. Thanks.'); - */ - reply (text) { - return this._post({ - uri: 'api/comment', - form: {api_type, text, thing_id: this.name} - }).tap(handleJsonErrors(this)).then(res => res.json.data.things[0]); - } - /** - * @summary Blocks the author of this content. - * @desc **Note:** In order for this function to have an effect, this item **must** be in the authenticated account's inbox or - modmail somewhere. The reddit API gives no outward indication of whether this condition is satisfied, so the returned Promise - will fulfill even if this is not the case. - * @returns {Promise} A Promise that fulfills with this message after the request is complete - * @example - * - * r.getInbox({limit: 1}).then(messages => - * messages[0].blockAuthor(); - * ); - */ - blockAuthor () { - return this._post({uri: 'api/block', form: {id: this.name}}).return(this); - } -}; - -export default ReplyableContent; diff --git a/src/objects/ReplyableContent.ts b/src/objects/ReplyableContent.ts new file mode 100644 index 00000000..9f136136 --- /dev/null +++ b/src/objects/ReplyableContent.ts @@ -0,0 +1,103 @@ +import RedditContent from './RedditContent' +import {handleJsonErrors} from '../helper' +import type {JSONResponse} from '../interfaces' + +const api_type = 'json' + +/** + * A set of mixin functions that apply to Submissions, Comments, and PrivateMessages + */ +class ReplyableContent> extends RedditContent { + static _name = 'ReplyableContent' + + /** + * @summary Approves this Comment, Submission, or PrivateMessage, re-adding it to public listings if it had been removed + * @returns A Promise that fulfills with this content when the request is complete + * @example r.getComment('c08pp5z').approve() + */ + async approve () { + await this._post({url: 'api/approve', form: {id: this.name}}) + return this + } + + /** + * @summary Blocks the author of this content. + * @desc **Note:** In order for this function to have an effect, this item **must** be in the authenticated account's inbox or + * modmail somewhere. The reddit API gives no outward indication of whether this condition is satisfied, so the returned Promise + * will fulfill even if this is not the case. + * @returns A Promise that fulfills with this message after the request is complete + * @example + * + * r.getInbox({limit: 1}).then(messages => + * messages[0].blockAuthor(); + * ); + */ + async blockAuthor () { + await this._post({url: 'api/block', form: {id: this.name}}) + return this + } + + /** + * @summary Ignores reports on this Comment, Submission, or PrivateMessage + * @returns A Promise that fulfills with this content when the request is complete + * @example r.getComment('c08pp5z').ignoreReports() + */ + async ignoreReports () { + await this._post({url: 'api/ignore_reports', form: {id: this.name}}) + return this + } + + /** + * @summary Removes this Comment, Submission or PrivateMessage from listings. + * @desc This requires the authenticated user to be a moderator of the subreddit with the `posts` permission. + * @param {boolean} [options.spam=false] Determines whether this should be marked as spam + * @returns A Promise that fulfills with this content when the request is complete + * @example r.getComment('c08pp5z').remove({spam: true}) + */ + async remove (spam = false) { + await this._post({url: 'api/remove', form: {spam, id: this.name}}) + return this + } + + /** + * @summary Submits a new reply to this object. (This takes the form of a new Comment if this object is a Submission/Comment, + * or a new PrivateMessage if this object is a PrivateMessage.) + * @param text The content of the reply, in raw markdown text + * @returns A Promise that fulfills with the newly-created reply + * @example r.getSubmission('4e60m3').reply('This was an interesting post. Thanks.'); + */ + async reply (text: string) { + const res: JSONResponse> = await this._post({ + url: 'api/comment', + form: {api_type, text, thing_id: this.name} + }) + handleJsonErrors(res) + return res.json.data!.things[0] + } + + /** + * @summary Reports this content anonymously to subreddit moderators (for Comments and Submissions) + * or to the reddit admins (for PrivateMessages) + * @param {string} [reason] The reason for the report + * @returns A Promise that fulfills with this content when the request is complete + * @example r.getComment('c08pp5z').report({reason: 'Breaking the subreddit rules'}) + */ + async report (reason?: string) { + await this._post({url: 'api/report', form: { + api_type, reason: 'other', other_reason: reason, thing_id: this.name + }}) + return this + } + + /** + * @summary Unignores reports on this Comment, Submission, or PrivateMessages + * @returns A Promise that fulfills with this content when the request is complete + * @example r.getComment('c08pp5z').unignoreReports() + */ + async unignoreReports () { + await this._post({url: 'api/unignore_reports', form: {id: this.name}}) + return this + } +} + +export default ReplyableContent diff --git a/src/objects/Submission.d.ts b/src/objects/Submission.d.ts deleted file mode 100644 index 07f82d8f..00000000 --- a/src/objects/Submission.d.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { Sort } from '../snoowrap'; -import Comment from './Comment'; -import Listing, { ListingOptions } from './Listing'; -import { FlairTemplate } from './Subreddit'; -import VoteableContent, { RichTextFlair } from './VoteableContent'; - -interface Media { - oembed?: { - /** The username of the uploader of the source media */ - author_name?: string; - /** URL to the author's profile on the source website */ - author_url?: string; - description?: string; - height: number; - html: string; - /** Name of the source website, e.g. "gfycat", "YouTube" */ - provider_name: string; - /** URL of the source website, e.g. "https://www.youtube.com" */ - provider_url: string; - thumbnail_height: number; - thumbnail_url: string; - thumbnail_width: number; - /** Name of the media on the content site, e.g. YouTube video title */ - title: string; - type: 'video' | 'rich'; - version: string; - width: number; - }; - reddit_video?: { - dash_url: string; - duration: number; - fallback_url: string; - height: number; - hls_url: string; - is_gif: boolean; - scrubber_media_url: string; - transcoding_status: string; - }; - type?: string; -} - -interface MediaEmbed { - /** HTML string of the media, usually an iframe */ - content?: string; - height?: number; - scrolling?: boolean; - width?: number; -} - -interface SecureMediaEmbed extends MediaEmbed { - media_domain_url?: string; -} - -export default class Submission extends VoteableContent { - clicked: boolean; - comments: Listing; - /** Categories for original content, e.g. "comics", "drawing_and_painting" */ - content_categories: string[] | null; - contest_mode: boolean; - domain: string; - hidden: boolean; - hide_score: boolean; - is_crosspostable: boolean; - is_meta: boolean; - is_original_content: boolean; - is_reddit_media_domain: boolean; - is_robot_indexable: boolean; - is_self: boolean; - is_video: boolean; - link_flair_background_color: string; - link_flair_css_class: string | null; - link_flair_richtext: RichTextFlair[]; - link_flair_template_id: string | null; - link_flair_text: string | null; - link_flair_text_color: 'dark' | 'light'; - link_flair_type: 'text' | 'richtext'; - locked: boolean; - media: Media | null; - media_embed: MediaEmbed; - media_only: boolean; - num_comments: number; - num_crossposts: number; - over_18: boolean; - parent_whitelist_status: string; - pinned: boolean; - previous_visits: number[]; - pwls: number; - post_hint: string; - preview: { enabled: boolean; images: ImagePreview[] }; - quarantine: boolean; - removal_reason: string | null; - removed_by_category: string | null; - /** Same content as media, except HTTPS */ - secure_media: Media | null; - secure_media_embed: SecureMediaEmbed; - selftext: string; - selftext_html: string | null; - spam?: boolean; - spoiler: boolean; - subreddit_subscribers: number; - suggested_sort: Sort | null; - thumbnail: string; - thumbnail_height?: number | null; - thumbnail_width?: number | null; - title: string; - upvote_ratio: number; - url: string; - view_count: number | null; - visited: boolean; - whitelist_status: string; - wls: number; - - assignFlair(options: { text: string; cssClass: string; }): Promise; - disableContestMode(): Promise; - enableContestMode(): Promise; - getDuplicates(options?: ListingOptions): Promise>; - getLinkFlairTemplates(): Promise; - /* @deprecated */ getRelated(options?: ListingOptions): Submission; - hide(): Promise; - lock(): Promise; - markAsRead(): Promise; - markNsfw(): Promise; - markSpoiler(): Promise; - selectFlair(options: { flair_template_id: string; text?: string; }): Promise; - setSuggestedSort(sort: Sort): Promise; - sticky(options?: { num?: number }): Promise; - unhide(): Promise; - unlock(): Promise; - unmarkNsfw(): Promise; - unmarkSpoiler(): Promise; - unsticky(): Promise; - submitCrosspost(): Promise; -} - -interface ImagePreviewSource { - url: string; - width: number; - height: number; -} - -interface ImagePreview { - source: ImagePreviewSource; - resolutions: ImagePreviewSource[]; - variants: any; // ? - id: string; -} diff --git a/src/objects/Submission.js b/src/objects/Submission.js deleted file mode 100644 index fab21d11..00000000 --- a/src/objects/Submission.js +++ /dev/null @@ -1,245 +0,0 @@ -import {getEmptyRepliesListing} from '../helpers.js'; -import VoteableContent from './VoteableContent.js'; - -const api_type = 'json'; - -/** -* A class representing a reddit submission -* -* @extends VoteableContent -* @example -* -* // Get a submission by ID -* r.getSubmission('2np694') -*/ -const Submission = class Submission extends VoteableContent { - constructor (data, _r, _hasFetched) { - super(data, _r, _hasFetched); - if (_hasFetched) { - this.comments = this.comments || getEmptyRepliesListing(this); - } - } - get _uri () { - return `comments/${this.name.slice(3)}`; - } - /** - * @summary Hides this Submission, preventing it from appearing on most Listings. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').hide() - */ - hide () { - return this._post({uri: 'api/hide', form: {id: this.name}}).return(this); - } - /** - * @summary Unhides this Submission, allowing it to reappear on most Listings. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').unhide() - */ - unhide () { - return this._post({uri: 'api/unhide', form: {id: this.name}}).return(this); - } - /** - * @summary Locks this Submission, preventing new comments from being posted on it. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').lock() - */ - lock () { - return this._post({uri: 'api/lock', form: {id: this.name}}).return(this); - } - /** - * @summary Unlocks this Submission, allowing comments to be posted on it again. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').unlock() - */ - unlock () { - return this._post({uri: 'api/unlock', form: {id: this.name}}).return(this); - } - /** - * @summary Marks this Submission as NSFW (Not Safe For Work). - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').markNsfw() - */ - markNsfw () { - return this._post({uri: 'api/marknsfw', form: {id: this.name}}).return(this); - } - /** - * @summary Unmarks this Submission as NSFW (Not Safe For Work). - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').unmarkNsfw() - */ - unmarkNsfw () { - return this._post({uri: 'api/unmarknsfw', form: {id: this.name}}).return(this); - } - /** - * @summary Mark a submission as a spoiler - * @desc **Note:** This will silently fail if the subreddit has disabled spoilers. - * @returns {Promise} A Promise that fulfills with this Submission when the request is complete - * @example r.getSubmission('2np694').markSpoiler() - */ - markSpoiler () { - return this._post({uri: 'api/spoiler', form: {id: this.name}}).return(this); - } - - /** - * @summary Unmark a submission as a spoiler - * @returns {Promise} A Promise that fulfills with this Submission when the request is complete - * @example r.getSubmission('2np694').unmarkSpoiler() - */ - unmarkSpoiler () { - return this._post({uri: 'api/unspoiler', form: {id: this.name}}).return(this); - } - - /** - * @summary Sets the contest mode status of this submission. - * @private - * @param {boolean} state The desired contest mode status - * @returns {Promise} The updated version of this Submission - */ - _setContestModeEnabled (state) { - return this._post({uri: 'api/set_contest_mode', form: {api_type, state, id: this.name}}).return(this); - } - /** - * @summary Enables contest mode for this Submission. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').enableContestMode() - */ - enableContestMode () { - return this._setContestModeEnabled(true); - } - /** - * @summary Disables contest mode for this Submission. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').disableContestMode() - */ - disableContestMode () { - return this._setContestModeEnabled(false); - } - _setStickied ({state, num}) { - return this._post({uri: 'api/set_subreddit_sticky', form: {api_type, state, num, id: this.name}}).return(this); - } - /** - * @summary Stickies this Submission. - * @param {object} [options] - * @param {number} [options.num=1] The sticky slot to put this submission in; this should be either 1 or 2. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').sticky({num: 2}) - */ - sticky ({num = 1} = {}) { - return this._setStickied({state: true, num}); - } - /** - * @summary Unstickies this Submission. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').unsticky() - */ - unsticky () { - return this._setStickied({state: false}); - } - /** - * @summary Sets the suggested comment sort method on this Submission - * @desc **Note**: To enable contest mode, use {@link Submission#enableContestMode} instead. - * @param {string} sort The suggested sort method. This should be one of - `confidence, top, new, controversial, old, random, qa, blank` - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').setSuggestedSort('new') - */ - setSuggestedSort (sort) { - return this._post({uri: 'api/set_suggested_sort', form: {api_type, id: this.name, sort}}).return(this); - } - /** - * @summary Marks this submission as 'visited'. - * @desc **Note**: This function only works if the authenticated account has a subscription to reddit gold. - * @returns {Promise} The updated version of this Submission - * @example r.getSubmission('2np694').markAsRead() - */ - markAsRead () { - return this._post({uri: 'api/store_visits', form: {links: this.name}}).return(this); - } - /** - * @summary Gets a Listing of other submissions on reddit that had the same link as this one. - * @param {object} [options={}] Options for the resulting Listing - * @returns {Promise} A Listing of other Submission objects - * @example r.getSubmission('2np694').getDuplicates() - */ - getDuplicates (options = {}) { - return this._getListing({uri: `duplicates/${this.name.slice(3)}`, qs: options}); - } - /** - * @summary Gets a Listing of Submissions that are related to this one. - * @deprecated This function uses the /related/submission_id endpoint, which was recently changed on reddit.com; - instead of returning a Listing containing related posts, the reddit API now simply returns the post itself. As such, this - function only exists for backwards compatability and should not be used in practice. - * @param {object} [options={}] ~~Options for the resulting Listing~~ - * @returns {Promise} ~~A Listing of other Submission objects~~ The submission in question. - * @example r.getSubmission('2np694').getRelated() - */ - getRelated (options = {}) { - return this._getListing({uri: `related/${this.name.slice(3)}`, qs: options}).tap(result => { - if (result.constructor._name === 'Submission') { - this._r._warn('Submission#getRelated has been deprecated upstream, and will not work as expected.'); - } - }); - } - /** - * @summary Gets a list of flair template options for this post. - * @returns {Promise} An Array of flair templates - * @example - * - * r.getSubmission('2np694').getLinkFlairTemplates().then(console.log) - * - * // => [ - * // { flair_text: 'Text 1', flair_css_class: '', flair_text_editable: false, flair_template_id: '(UUID not shown)' ... }, - * // { flair_text: 'Text 2', flair_css_class: 'aa', flair_text_editable: false, flair_template_id: '(UUID not shown)' ... }, - * // ... - * // ] - */ - getLinkFlairTemplates () { - return this.fetch().get('subreddit').then(sub => sub.getLinkFlairTemplates(this.name)); - } - /** - * @summary Assigns flair on this Submission (as a moderator; also see [selectFlair]{@link Submission#selectFlair}) - * @param {object} options - * @param {string} options.text The text that this link's flair should have - * @param {string} options.cssClass The CSS class that the link's flair should have - * @returns {Promise} A Promise that fulfills with an updated version of this Submission - * @example r.getSubmission('2np694').assignFlair({text: 'this is a flair text', cssClass: 'these are css classes'}) - */ - assignFlair (options) { - return this._r._assignFlair({...options, link: this.name, subredditName: this.subreddit.display_name}).return(this); - } - - /** - * @summary Selects a flair for this Submission (as the OP; also see [assignFlair]{@link Submission#assignFlair}) - * @param {object} options - * @param {string} options.flair_template_id A flair template ID to use for this Submission. (This should be obtained - beforehand using {@link getLinkFlairTemplates}.) - * @param {string} [options.text] The flair text to use for the submission. (This is only necessary/useful if the given flair - template has the `text_editable` property set to `true`.) - * @returns {Promise} A Promise that fulfills with this objects after the request is complete - * @example r.getSubmission('2np694').selectFlair({flair_template_id: 'e3340d80-8152-11e4-a76a-22000bc1096c'}) - */ - selectFlair (options) { - return this._r._selectFlair({...options, link: this.name, subredditName: this.subreddit.display_name}).return(this); - } - - /** - * @summary Crossposts this submission to a different subreddit - * @desc **NOTE**: To create a crosspost, the authenticated account must be subscribed to the subreddit where - * the crosspost is being submitted, and that subreddit be configured to allow crossposts. - * @param {object} options An object containing details about the submission - * @param {string} options.subredditName The name of the subreddit that the crosspost should be submitted to - * @param {string} options.title The title of the crosspost - * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission - * @param {boolean} [options.resubmit=true] If this is false and same link has already been submitted to this subreddit in - the past, reddit will return an error. This could be used to avoid accidental reposts. - * @returns {Promise} The newly-created Submission object - * @example - * - * await r.getSubmission('6vths0').submitCrosspost({ title: 'I found an interesting post', subredditName: 'snoowrap' }) - */ - submitCrosspost (options) { - return this._r.submitCrosspost({...options, originalPost: this}); - } -}; - -export default Submission; diff --git a/src/objects/Submission.ts b/src/objects/Submission.ts new file mode 100644 index 00000000..5f63ae87 --- /dev/null +++ b/src/objects/Submission.ts @@ -0,0 +1,412 @@ +import {getEmptyRepliesListing, addFullnamePrefix} from '../helper' +import Comment from './Comment' +import VoteableContent from './VoteableContent' +import type snoowrap from '../snoowrap' +import type {Listing} from './' +import type {FetchMoreOptions, FetchAllOptions, ListingQuery} from './Listing' +import type { + OmitProps, RichTextFlair, Media, MediaEmbed, SecureMediaEmbed, ImagePreview, + SubmitCrosspostOptions, AssignFlairOptions, SelectFlairOptions +} from '../interfaces' +import type {COMMENT_SORTS} from '../constants' + +const api_type = 'json' + + +interface StickyOptions { + num?: number + state: boolean + [key: string]: any +} + +interface Submission { + clicked: boolean + comments: Listing + /** Categories for original content, e.g. "comics", "drawing_and_painting" */ + content_categories: string[]|null + contest_mode: boolean + created: number + created_utc: number + domain: string + hidden: boolean + hide_score: boolean + id: string + is_crosspostable: boolean + is_meta: boolean + is_original_content: boolean + is_reddit_media_domain: boolean + is_robot_indexable: boolean + is_self: boolean + is_video: boolean + link_flair_background_color: string + link_flair_css_class: string|null + link_flair_richtext: RichTextFlair[] + link_flair_template_id: string|null + link_flair_text: string|null + link_flair_text_color: 'dark'|'light' + link_flair_type: 'text'|'richtext' + media: Media|null + media_embed: MediaEmbed + media_only: boolean; + name: string + num_comments: number + num_crossposts: number + over_18: boolean + parent_whitelist_status: string + pinned: boolean + previous_visits: number[] + pwls: number + post_hint: string + preview: { enabled: boolean; images: ImagePreview[] } + quarantine: boolean + removal_reason: string|null + removed_by_category: string|null + /** Same content as media, except HTTPS */ + secure_media: Media|null + secure_media_embed: SecureMediaEmbed + selftext: string + selftext_html: string|null + spam?: boolean + spoiler: boolean + subreddit_subscribers: number + suggested_sort: typeof COMMENT_SORTS[number]|null + thumbnail: string + thumbnail_height?: number|null + thumbnail_width?: number|null + title: string + upvote_ratio: number + url: string + view_count: number|null + visited: boolean + whitelist_status: string + wls: number +} + +/** + * A class representing a reddit submission + * @example + * + * // Get a submission by ID + * r.getSubmission('2np694') + */ +class Submission extends VoteableContent { + static _name = 'Submission' + + _sort?: typeof COMMENT_SORTS[number] + _children: {[id: string]: Comment} + + constructor ( + options: {[key: string]: any}, + _r: snoowrap, + _hasFetched = false + ) { + super(options, _r, _hasFetched) + this._callback = this._callback.bind(this) + this._children = {} + if (_hasFetched) { + this.comments = this.comments || getEmptyRepliesListing(this) + } + } + _transformApiResponse (response: Submission) { + response._sort = this._sort + for (const id in response._children) { + const child = response._children[id] + child._sort = response._sort + child._cb = response._callback + } + return response + } + /** + * A function used to cache children to the `Submission._children` property. By "children" we mean + * all nested comments/replies that belong to the submission. `Submission._children` used by + * the function `Submission.getComment()` to get children that are in deep chains. We pass this function + * to children as `Comment._cb()`. + */ + _callback (child: {_children: {[id: string]: Comment}}) { + if (child instanceof Comment) { + const parent = child.parent_id.startsWith('t1_') ? this._children[child.parent_id.slice(3)] : this + if (parent) { + const listing: Listing = parent.replies || parent.comments + const index = listing.findIndex(c => c.id === child.id) + if (index !== -1) { + listing[index] = child + } + } + child._children[child.id] = child + this._callback({_children: child._children}) + } else { + for (const id in child._children) { + child._children[id]._sort = this._sort + child._children[id]._cb = this._callback + } + Object.assign(this._children, child._children) + } + } + get _uri () { + return `comments/${this.name.slice(3)}${this._sort ? `?sort=${this._sort}` : ''}` + } + /** + * @summary Pick a comment from the comments tree or fetch it with a given id. + * @param {string} commentId - The base36 id of the comment + * @param {boolean} [fetch] - If true, this function will return an unfetched Comment object + * instead. Calling `.fetch()` will make it replace the one with the same id on the tree if exists. + * It will also expose all the children on its replies tree to this function. + * @returns {Comment|null} A Comment object for the requested comment, or `null` when it's not available + * on the comments tree. + * @example + * + * const og = submission.comments[0].replies[0] + * const comment = submission.getComment(og.id) + * console.log(comment === og) + * // => true + */ + getComment (commentId: string, fetch: boolean) { + let comment = this._children[commentId] || null + if (fetch) { + comment = this._r._newObject('Comment', { + name: addFullnamePrefix(commentId, 't1_'), + link_id: this.name, + _sort: this._sort, + _cb: this._callback + }) + } + return comment + } + /** + * @summary Fetch more comments and append them automatically to the comments listing. All comments and their + * children will be exposed automatically to {@link Submission#getComment}. + * @param options - Object of fetching options or the number of comments to fetch. see + * {@link Listing#fetchMore} for more details. + * @returns A Promise that fulfills with the comments listing. + */ + async fetchMore (options: Partial>|number) { + if (typeof options !== 'number') { + options.append = true + } + const comments = await this.comments.fetchMore(options) + this._callback({_children: comments._children}) + this.comments = comments + return comments + } + /** + * @summary Fetch all comments and append them automatically to the comments listing. All comments and their + * children will be exposed automatically to {@link Submission#getComment}. + * @param {object} [options] - Fetching options. see {@link Listing#fetchAll} for more details. + * @returns A Promise that fulfills with the comments listing. + */ + fetchAll (options?: OmitProps) { + return this.fetchMore({...options, amount: Infinity}) + } + /** + * @summary Hides this Submission, preventing it from appearing on most Listings. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').hide() + */ + async hide () { + await this._post({url: 'api/hide', form: {id: this.name}}) + return this + } + /** + * @summary Unhides this Submission, allowing it to reappear on most Listings. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').unhide() + */ + async unhide () { + await this._post({url: 'api/unhide', form: {id: this.name}}) + return this + } + /** + * @summary Locks this Submission, preventing new comments from being posted on it. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').lock() + */ + async lock () { + await this._post({url: 'api/lock', form: {id: this.name}}) + return this + } + /** + * @summary Unlocks this Submission, allowing comments to be posted on it again. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').unlock() + */ + async unlock () { + await this._post({url: 'api/unlock', form: {id: this.name}}) + return this + } + /** + * @summary Marks this Submission as NSFW (Not Safe For Work). + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').markNsfw() + */ + async markNsfw () { + await this._post({url: 'api/marknsfw', form: {id: this.name}}) + return this + } + /** + * @summary Unmarks this Submission as NSFW (Not Safe For Work). + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').unmarkNsfw() + */ + async unmarkNsfw () { + await this._post({url: 'api/unmarknsfw', form: {id: this.name}}) + return this + } + /** + * @summary Mark a submission as a spoiler + * @desc **Note:** This will silently fail if the subreddit has disabled spoilers. + * @returns A Promise that fulfills with this Submission when the request is complete + * @example r.getSubmission('2np694').markSpoiler() + */ + async markSpoiler () { + await this._post({url: 'api/spoiler', form: {id: this.name}}) + return this + } + + /** + * @summary Unmark a submission as a spoiler + * @returns A Promise that fulfills with this Submission when the request is complete + * @example r.getSubmission('2np694').unmarkSpoiler() + */ + async unmarkSpoiler () { + await this._post({url: 'api/unspoiler', form: {id: this.name}}) + return this + } + + /** + * @summary Sets the contest mode status of this submission. + * @private + * @param {boolean} state The desired contest mode status + * @returns The updated version of this Submission + */ + async _setContestModeEnabled (state: boolean) { + await this._post({url: 'api/set_contest_mode', form: {api_type, state, id: this.name}}) + return this + } + /** + * @summary Enables contest mode for this Submission. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').enableContestMode() + */ + enableContestMode () { + return this._setContestModeEnabled(true) + } + /** + * @summary Disables contest mode for this Submission. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').disableContestMode() + */ + disableContestMode () { + return this._setContestModeEnabled(false) + } + async _setStickied ({num, state, ...opts}: StickyOptions) { + await this._post({url: 'api/set_subreddit_sticky', form: {...opts, api_type, num, state, id: this.name}}) + return this + } + /** + * @summary Stickies this Submission. + * @param {object} [options] + * @param {number} [options.num=1] The sticky slot to put this submission in; this should be either 1 or 2. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').sticky({num: 2}) + */ + async sticky ({num = 1} = {}) { + await this._setStickied({num, state: true}) + return this + } + /** + * @summary Unstickies this Submission. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').unsticky() + */ + async unsticky () { + await this._setStickied({state: false}) + return this + } + /** + * @summary Sets the suggested comment sort method on this Submission + * @desc **Note**: To enable contest mode, use {@link Submission#enableContestMode} instead. + * @param {string} sort The suggested sort method. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').setSuggestedSort('new') + */ + async setSuggestedSort (sort?: typeof COMMENT_SORTS[number]) { + await this._post({url: 'api/set_suggested_sort', form: {api_type, id: this.name, sort}}) + return this + } + /** + * @summary Marks this submission as 'visited'. + * @desc **Note**: This function only works if the authenticated account has a subscription to reddit gold. + * @returns The updated version of this Submission + * @example r.getSubmission('2np694').markAsRead() + */ + async markAsRead () { + await this._post({url: 'api/store_visits', form: {links: this.name}}) + return this + } + /** + * @summary Gets a Listing of other submissions on reddit that had the same link as this one. + * @param {object} [options={}] Options for the resulting Listing + * @returns A Listing of other Submission objects + * @example r.getSubmission('2np694').getDuplicates() + */ + getDuplicates (options: ListingQuery = {}) { + return this._getListing({uri: `duplicates/${this.name.slice(3)}`, qs: options}) + } + /** + * @summary Gets a list of flair template options for this post. + * @returns An Array of flair templates + * @example + * + * r.getSubmission('2np694').getLinkFlairTemplates().then(console.log) + * + * // => [ + * // { flair_text: 'Text 1', flair_css_class: '', flair_text_editable: false, flair_template_id: '(UUID not shown)' ... }, + * // { flair_text: 'Text 2', flair_css_class: 'aa', flair_text_editable: false, flair_template_id: '(UUID not shown)' ... }, + * // ... + * // ] + */ + async getLinkFlairTemplates () { + await this.fetch() + return this.subreddit.getLinkFlairTemplates(this.name) + } + /** + * @summary Assigns flair on this Submission (as a moderator; also see [selectFlair]{@link Submission#selectFlair}) + * @param {object} options + * @returns A Promise that fulfills with an updated version of this Submission + * @example r.getSubmission('2np694').assignFlair({text: 'this is a flair text', cssClass: 'these are css classes'}) + */ + async assignFlair (options: OmitProps) { + await this.fetch() + await this._r._assignFlair({...options, link: this.name, subredditName: this.subreddit.display_name}) + return this + } + + /** + * @summary Selects a flair for this Submission (as the OP; also see [assignFlair]{@link Submission#assignFlair}) + * @param options + * @returns A Promise that fulfills with this objects after the request is complete + * @example r.getSubmission('2np694').selectFlair({flair_template_id: 'e3340d80-8152-11e4-a76a-22000bc1096c'}) + */ + async selectFlair (options: OmitProps) { + await this.fetch() + await this._r._selectFlair({...options, link: this.name, subredditName: this.subreddit.display_name}) + return this + } + + /** + * @summary Crossposts this submission to a different subreddit + * @desc **NOTE**: To create a crosspost, the authenticated account must be subscribed to the subreddit where + * the crosspost is being submitted, and that subreddit be configured to allow crossposts. + * @param options An object containing details about the submission + * @returns The newly-created Submission object + * @example + * + * await r.getSubmission('6vths0').submitCrosspost({ title: 'I found an interesting post', subredditName: 'snoowrap' }) + */ + submitCrosspost (options: OmitProps) { + return this._r.submitCrosspost({...options, originalPost: this}) + } +} + +export default Submission +export {StickyOptions} diff --git a/src/objects/Subreddit.d.ts b/src/objects/Subreddit.d.ts index 32f3035d..84d28839 100644 --- a/src/objects/Subreddit.d.ts +++ b/src/objects/Subreddit.d.ts @@ -1,15 +1,16 @@ -import { BaseSearchOptions, ModAction, Sort, SubmitLinkOptions, SubmitSelfPostOptions } from '../snoowrap'; +// @ts-nocheck +import {BaseSearchOptions, ModAction, Sort, SubmitLinkOptions, SubmitSelfPostOptions} from '../snoowrap'; import Comment from './Comment'; -import Listing, { ListingOptions } from './Listing'; +import Listing, {ListingOptions} from './Listing'; import PrivateMessage from './PrivateMessage'; import RedditContent from './RedditContent'; import RedditUser from './RedditUser'; import Submission from './Submission'; -import { RichTextFlair } from './VoteableContent'; -import WikiPage, { WikiPageRevision } from './WikiPage'; +import {RichTextFlair} from './VoteableContent'; +import WikiPage, {WikiPageRevision} from './WikiPage'; import ModmailConversation from './ModmailConversation'; -export default class Subreddit extends RedditContent { +export default class Subreddit extends RedditContent { accounts_active_is_fuzzed: boolean; accounts_active: number; active_user_count: number; @@ -220,6 +221,7 @@ type SubredditType = 'public' | 'private' | 'restricted' | 'gold_restricted' | ' type LinkType = 'any' | 'link' | 'self'; type SpamLevel = 'low' | 'high' | 'all'; + export interface SubredditSettings { name: string; title: string; diff --git a/src/objects/Subreddit.js b/src/objects/Subreddit.js deleted file mode 100644 index ffc879cd..00000000 --- a/src/objects/Subreddit.js +++ /dev/null @@ -1,1340 +0,0 @@ -import {chunk, flatten, map, omit} from 'lodash'; -import Promise from '../Promise.js'; -import {Readable} from 'stream'; -import {createReadStream} from 'fs'; -import {formatModPermissions, handleJsonErrors, renameKey} from '../helpers.js'; -import {InvalidMethodCallError} from '../errors.js'; -import RedditContent from './RedditContent.js'; - -const api_type = 'json'; - -/** -* A class representing a subreddit -* -* @extends RedditContent -* @example -* -* // Get a subreddit by name -* r.getSubreddit('AskReddit') -*/ -const Subreddit = class Subreddit extends RedditContent { - get _uri () { - return `r/${this.display_name}/about`; - } - _transformApiResponse (response) { - if (!(response instanceof Subreddit)) { - throw new TypeError(`The subreddit /r/${this.display_name} does not exist.`); - } - return response; - } - _deleteFlairTemplates ({flair_type}) { - return this._post({uri: `r/${this.display_name}/api/clearflairtemplates`, form: {api_type, flair_type}}).return(this); - } - /** - * @summary Deletes all of this subreddit's user flair templates - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteAllUserFlairTemplates() - */ - deleteAllUserFlairTemplates () { - return this._deleteFlairTemplates({flair_type: 'USER_FLAIR'}); - } - /** - * @summary Deletes all of this subreddit's link flair templates - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteAllLinkFlairTemplates() - */ - deleteAllLinkFlairTemplates () { - return this._deleteFlairTemplates({flair_type: 'LINK_FLAIR'}); - } - /** - * @summary Deletes one of this subreddit's flair templates - * @param {object} options - * @param {string} options.flair_template_id The ID of the template that should be deleted - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteFlairTemplate({flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721'}) - */ - deleteFlairTemplate ({flair_template_id}) { - return this._post({ - uri: `r/${this.display_name}/api/deleteflairtemplate`, - form: {api_type, flair_template_id} - }).return(this); - } - _createFlairTemplate ({ - text, css_class, cssClass = css_class, flair_type, text_editable = false, textEditable = text_editable - }) { - return this._post({ - uri: `r/${this.display_name}/api/flairtemplate`, - form: {api_type, text, css_class: cssClass, flair_type, text_editable: textEditable} - }).return(this); - } - /** - * @summary Creates a new user flair template for this subreddit - * @param {object} options - * @param {string} options.text The flair text for this template - * @param {string} [options.cssClass=''] The CSS class for this template - * @param {boolean} [options.textEditable=false] Determines whether users should be able to edit their flair text - when it has this template - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').createUserFlairTemplate({text: 'Some Flair Text', cssClass: 'some-css-class'}) - */ - createUserFlairTemplate (options) { - return this._createFlairTemplate({...options, flair_type: 'USER_FLAIR'}); - } - /** - * @summary Creates a new link flair template for this subreddit - * @param {object} options - * @param {string} options.text The flair text for this template - * @param {string} [options.cssClass=''] The CSS class for this template - * @param {boolean} [options.textEditable=false] Determines whether users should be able to edit the flair text of their - links when it has this template - * @returns {Promise} A Promise that fulfills with this Subredit when the request is complete. - * @example r.getSubreddit('snoowrap').createLinkFlairTemplate({text: 'Some Flair Text', cssClass: 'some-css-class'}) - */ - createLinkFlairTemplate (options) { - return this._createFlairTemplate({...options, flair_type: 'LINK_FLAIR'}); - } - _getFlairOptions ({name, link, is_newlink} = {}) { // TODO: Add shortcuts for this on RedditUser and Submission - return this._post({uri: `r/${this.display_name}/api/flairselector`, form: {name, link, is_newlink}}); - } - /** - * @summary Gets the flair templates for the subreddit or a given link. - * @param {string} [linkId] The link's base36 ID - * @returns {Promise} An Array of flair template options - * @example - * - * r.getSubreddit('snoowrap').getLinkFlairTemplates('4fp36y').then(console.log) - // => [ { flair_css_class: '', - // flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721', - // flair_text_editable: true, - // flair_position: 'right', - // flair_text: '' }, - // { flair_css_class: '', - // flair_template_id: '03821f62-c920-11e5-b608-0e309fbcf863', - // flair_text_editable: true, - // flair_position: 'right', - // flair_text: '' }, - // ... - // ] - */ - getLinkFlairTemplates (linkId = null) { - const options = linkId ? {link: linkId} : {is_newlink: true}; - return this._getFlairOptions(options).get('choices'); - } - /** - * @summary Gets the list of user flair templates on this subreddit. - * @returns {Promise} An Array of user flair templates - * @example - * - * r.getSubreddit('snoowrap').getUserFlairTemplates().then(console.log) - // => [ { flair_css_class: '', - // flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721', - // flair_text_editable: true, - // flair_position: 'right', - // flair_text: '' }, - // { flair_css_class: '', - // flair_template_id: '03821f62-c920-11e5-b608-0e309fbcf863', - // flair_text_editable: true, - // flair_position: 'right', - // flair_text: '' }, - // ... - // ] - */ - getUserFlairTemplates () { - return this._getFlairOptions().get('choices'); - } - /** - * @summary Clears a user's flair on this subreddit. - * @param {string} name The user's name - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteUserFlair('actually_an_aardvark') - */ - deleteUserFlair (name) { - return this._post({uri: `r/${this.display_name}/api/deleteflair`, form: {api_type, name}}).return(this); - } - /** - * @summary Gets a user's flair on this subreddit. - * @param {string} name The user's name - * @returns {Promise} An object representing the user's flair - * @example - * - * r.getSubreddit('snoowrap').getUserFlair('actually_an_aardvark').then(console.log) - // => { flair_css_class: '', - // flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721', - // flair_text: '', - // flair_position: 'right' - // } - */ - getUserFlair (name) { - return this._getFlairOptions({name}).get('current'); - } - /** - * @summary Sets multiple user flairs at the same time - * @desc Due to the behavior of the reddit API endpoint that this function uses, if any of the provided user flairs are - invalid, reddit will make note of this in its response, but it will still attempt to set the remaining user flairs. If this - occurs, the Promise returned by snoowrap will be rejected, and the rejection reason will be an array containing the 'error' - responses from reddit. - * @param {object[]} flairArray - * @param {string} flairArray[].name A user's name - * @param {string} flairArray[].text The flair text to assign to this user - * @param {string} flairArray[].cssClass The flair CSS class to assign to this user - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example - * r.getSubreddit('snoowrap').setMultipleUserFlairs([ - * {name: 'actually_an_aardvark', text: "this is /u/actually_an_aardvark's flair text", cssClass: 'some-css-class'}, - * {name: 'snoowrap_testing', text: "this is /u/snoowrap_testing's flair text", cssClass: 'some-css-class'} - * ]); - * // the above request gets completed successfully - * - * r.getSubreddit('snoowrap').setMultipleUserFlairs([ - * {name: 'actually_an_aardvark', text: 'foo', cssClass: 'valid-css-class'}, - * {name: 'snoowrap_testing', text: 'bar', cssClass: "this isn't a valid css class"}, - * {name: 'not_an_aardvark', text: 'baz', cssClass: "this also isn't a valid css class"} - * ]) - * // the Promise from the above request gets rejected, with the following rejection reason: - * [ - * { - * status: 'skipped', - * errors: { css: 'invalid css class `this isn\'t a valid css class\', ignoring' }, - * ok: false, - * warnings: {} - * }, - * { - * status: 'skipped', - * errors: { css: 'invalid css class `this also isn\'t a valid css class\', ignoring' }, - * ok: false, - * warnings: {} - * } - * ] - * // note that /u/actually_an_aardvark's flair still got set by the request, even though the other two flairs caused errors. - */ - setMultipleUserFlairs (flairArray) { - const csvLines = flairArray.map(item => { - // reddit expects to receive valid CSV data, which each line having the form `username,flair_text,css_class`. - return [ - item.name, item.text || item.flairText || item.flair_text || '', - item.cssClass || item.css_class || item.flairCssClass || item.flair_css_class || '' - ].map(str => { - /* To escape special characters in the lines (e.g. if the flair text itself contains a comma), surround each - part of the line with double quotes before joining the parts together with commas (in accordance with how special - characters are usually escaped in CSV). If double quotes are themselves part of the flair text, replace them with a - pair of consecutive double quotes. */ - return `"${str.replace(/"/g, '""')}"`; - }).join(','); - }); - /* Due to an API limitation, this endpoint can only set the flair of 100 users at a time. - Send multiple requests if necessary to ensure that all users in the array are accounted for. */ - return Promise.map(chunk(csvLines, 100), flairChunk => { - return this._post({uri: `r/${this.display_name}/api/flaircsv`, form: {flair_csv: flairChunk.join('\n')}}); - }).then(flatten).tap(results => { - const errorRows = results.filter(row => !row.ok); - if (errorRows.length) { - throw errorRows; - } - }).return(this); - } - /** - * @summary Gets a list of all user flairs on this subreddit. - * @param {object} options - * @param {string} [options.name] A specific username to jump to - * @returns {Promise} A Listing containing user flairs - * @example - * - * r.getSubreddit('snoowrap').getUserFlairList().then(console.log) - // => Listing [ - // { flair_css_class: null, - // user: 'not_an_aardvark', - // flair_text: 'Isn\'t an aardvark' }, - // { flair_css_class: 'some-css-class', - // user: 'actually_an_aardvark', - // flair_text: 'this is /u/actually_an_aardvark\'s flair text' }, - // { flair_css_class: 'some-css-class', - // user: 'snoowrap_testing', - // flair_text: 'this is /u/snoowrap_testing\'s flair text' } - // ] - */ - getUserFlairList (options = {}) { - return this._getListing({uri: `r/${this.display_name}/api/flairlist`, qs: options, _transform: response => { - /* For unknown reasons, responses from the api/flairlist endpoint are formatted differently than responses from all other - Listing endpoints. Most Listing endpoints return an object with a `children` property containing the Listing's children, - and `after` and `before` properties corresponding to the `after` and `before` querystring parameters that a client should - use in the next request. However, the api/flairlist endpoint returns an objecti with a `users` property containing the - Listing's children, and `next` and `prev` properties corresponding to the `after` and `before` querystring parameters. As - far as I can tell, there's no actual reason for this difference. >_> */ - response.after = response.next || null; - response.before = response.prev || null; - response.children = response.users; - return this._r._newObject('Listing', response); - }}); - } - /** - * @summary Configures the flair settings for this subreddit. - * @param {object} options - * @param {boolean} options.userFlairEnabled Determines whether user flair should be enabled - * @param {string} options.userFlairPosition Determines the orientation of user flair relative to a given username. This - should be either the string 'left' or the string 'right'. - * @param {boolean} options.userFlairSelfAssignEnabled Determines whether users should be able to edit their own flair - * @param {string} options.linkFlairPosition Determines the orientation of link flair relative to a link title. This should - be either 'left' or 'right'. - * @param {boolean} options.linkFlairSelfAssignEnabled Determines whether users should be able to edit the flair of their - submissions. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').configure_flair({ - userFlairEnabled: true, - userFlairPosition: 'left', - userFlairSelfAssignEnabled: false, - linkFlairPosition: 'right', - linkFlairSelfAssignEnabled: false - * }) - */ - configureFlair ({ - user_flair_enabled, userFlairEnabled = user_flair_enabled, - user_flair_position, userFlairPosition = user_flair_position, - user_flair_self_assign_enabled, userFlairSelfAssignEnabled = user_flair_self_assign_enabled, - link_flair_position, linkFlairPosition = link_flair_position, - link_flair_self_assign_enabled, linkFlairSelfAssignEnabled = link_flair_self_assign_enabled - }) { - return this._post({uri: `r/${this.display_name}/api/flairconfig`, form: { - api_type, - flair_enabled: userFlairEnabled, - flair_position: userFlairPosition, - flair_self_assign_enabled: userFlairSelfAssignEnabled, - link_flair_position: linkFlairPosition, - link_flair_self_assign_enabled: linkFlairSelfAssignEnabled - }}).return(this); - } - /** - * @summary Gets the requester's flair on this subreddit. - * @returns {Promise} An object representing the requester's current flair - * @example - * - * r.getSubreddit('snoowrap').getMyFlair().then(console.log) - // => { flair_css_class: 'some-css-class', - // flair_template_id: null, - // flair_text: 'this is /u/snoowrap_testing\'s flair text', - // flair_position: 'right' - // } - */ - getMyFlair () { - return this._getFlairOptions().get('current'); - } - /** - * @summary Sets the requester's flair on this subreddit. - * @param {object} options - * @param {string} options.flair_template_id A flair template ID to use. (This should be obtained beforehand using - {@link getUserFlairTemplates}.) - * @param {string} [options.text] The flair text to use. (This is only necessary/useful if the given flair - template has the `text_editable` property set to `true`.) - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').selectMyFlair({flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721'}) - */ - selectMyFlair (options) { - /* NOTE: This requires `identity` scope in addition to `flair` scope, since the reddit api needs to be passed a username. - I'm not sure if there's a way to do this without requiring additional scope. */ - return this._r._getMyName().then(name => { - return this._r._selectFlair({...options, subredditName: this.display_name, name}); - }).return(this); - } - _setMyFlairVisibility (flair_enabled) { - return this._post({uri: `r/${this.display_name}/api/setflairenabled`, form: {api_type, flair_enabled}}).return(this); - } - /** - * @summary Makes the requester's flair visible on this subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').showMyFlair() - */ - showMyFlair () { - return this._setMyFlairVisibility(true); - } - /** - * @summary Makes the requester's flair invisible on this subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').hideMyFlair() - */ - hideMyFlair () { - return this._setMyFlairVisibility(false); - } - /** - * @summary Creates a new selfpost on this subreddit. - * @param {object} options An object containing details about the submission - * @param {string} options.title The title of the submission - * @param {string} [options.text] The selftext of the submission - * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission - * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account - requires a captcha to submit posts and comments. - * @param {string} [options.captchaResponse] The response to the captcha with the given identifier - * @returns {Promise} The newly-created Submission object - * @example - * - * r.getSubreddit('snoowrap').submitSelfpost({title: 'this is a selfpost', text: "hi, how's it going?"}).then(console.log) - * // => Submission { name: 't3_4abmsz' } - */ - submitSelfpost (options) { - return this._r.submitSelfpost({...options, subredditName: this.display_name}); - } - /** - * @summary Creates a new link submission on this subreddit. - * @param {object} options An object containing details about the submission - * @param {string} options.title The title of the submission - * @param {string} options.url The url that the link submission should point to - * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission - * @param {boolean} [options.resubmit=true] If this is false and same link has already been submitted to this subreddit in - the past, reddit will return an error. This could be used to avoid accidental reposts. - * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account - requires a captcha to submit posts and comments. - * @param {string} [options.captchaResponse] The response to the captcha with the given identifier - * @returns {Promise} The newly-created Submission object - * @example - * - * r.getSubreddit('snoowrap').submitLink({title: 'I found a cool website', url: 'https://google.com'}).then(console.log) - * // => Submission { name: 't3_4abmsz' } - */ - submitLink (options) { - return this._r.submitLink({...options, subredditName: this.display_name}); - } - - /** - * @summary Creates a new crosspost submission on this subreddit - * @desc **NOTE**: To create a crosspost, the authenticated account must be subscribed to the subreddit where - * the crosspost is being submitted, and that subreddit be configured to allow crossposts. - * @param {object} options An object containing details about the submission - * @param {string} options.title The title of the crosspost - * @param {string|Submission} options.originalPost A Submission object or a post ID for the original post which - is being crossposted - * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission - * @param {boolean} [options.resubmit=true] If this is false and same link has already been submitted to this subreddit in - the past, reddit will return an error. This could be used to avoid accidental reposts. - * @returns {Promise} The newly-created Submission object - * @example - * - * await r.getSubreddit('snoowrap').submitCrosspost({ title: 'I found an interesting post', originalPost: '6vths0' }) - * // => Submission { name: 't3_4abmsz' } - */ - submitCrosspost (options) { - return this._r.submitCrosspost({...options, subredditName: this.display_name}); - } - - /** - * @summary Gets a Listing of hot posts on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @returns {Promise} A Listing containing the retrieved submissions - * @example - * - * r.getSubreddit('snoowrap').getHot().then(console.log) - * // => Listing [ - * // Submission { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getHot (options) { - return this._r.getHot(this.display_name, options); - } - /** - * @summary Gets a Listing of new posts on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @returns {Promise} A Listing containing the retrieved submissions - * @example - * - * r.getSubreddit('snoowrap').getNew().then(console.log) - * // => Listing [ - * // Submission { ... }, - * // Submission { ... }, - * // ... - * // ] - * - */ - getNew (options) { - return this._r.getNew(this.display_name, options); - } - /** - * @summary Gets a Listing of new comments on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @returns {Promise} A Listing containing the retrieved comments - * @example - * - * r.getSubreddit('snoowrap').getNewComments().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // ... - * // ] - */ - getNewComments (options) { - return this._r.getNewComments(this.display_name, options); - } - /** - * @summary Gets a single random Submission from this subreddit. - * @desc **Note**: This function will not work when snoowrap is running in a browser, because the reddit server sends a - redirect which cannot be followed by a CORS request. - * @returns {Promise} The retrieved Submission object - * @example - * - * r.getSubreddit('snoowrap').getRandomSubmission().then(console.log) - * // => Submission { ... } - */ - getRandomSubmission () { - return this._r.getRandomSubmission(this.display_name); - } - /** - * @summary Gets a Listing of top posts on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string} [options.time] Describes the timespan that posts should be retrieved from. Should be one of - `hour, day, week, month, year, all` - * @returns {Promise} A Listing containing the retrieved submissions - * @example - * - * r.getSubreddit('snoowrap').getTop({time: 'all'}).then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // ... - * // ] - */ - getTop (options) { - return this._r.getTop(this.display_name, options); - } - /** - * @summary Gets a Listing of controversial posts on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string} [options.time] Describes the timespan that posts should be retrieved from. Should be one of - `hour, day, week, month, year, all` - * @returns {Promise} A Listing containing the retrieved submissions - * @example - * - * r.getSubreddit('snoowrap').getControversial({time: 'week'}).then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // ... - * // ] - */ - getControversial (options) { - return this._r.getControversial(this.display_name, options); - } - /** - * @summary Gets a Listing of top posts on this subreddit. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing the retrieved submissions - * @example - * - * r.getSubreddit('snoowrap').getRising().then(console.log) - * // => Listing [ - * // Submission { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getRising (options) { - return this._r.getRising(this.display_name, options); - } - /** - * @summary Gets the moderator mail for this subreddit. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing PrivateMessage objects - * @example r.getSubreddit('snoowrap').getModmail().then(console.log) - */ - getModmail (options) { - return this._getListing({uri: `r/${this.display_name}/about/message/moderator`, qs: options}); - } - /** - * @summary Gets a list of ModmailConversations from the subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @returns {Promise>} A Listing containing Subreddits - * @example - * - * r.getSubreddit('snoowrap').getNewModmailConversations({limit: 2}).then(console.log) - * // => Listing [ - * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... }, - * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... } - * // ] - */ - getNewModmailConversations (options = {}) { - return this._r.getNewModmailConversations({...options, entity: this.display_name}); - } - /** - * @summary Gets the moderation log for this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string[]} [options.mods] An array of moderator names that the results should be restricted to - * @param {string} [options.type] Restricts the results to the specified type. This should be one of `banuser, unbanuser, - removelink, approvelink, removecomment, approvecomment, addmoderator, invitemoderator, uninvitemoderator, - acceptmoderatorinvite, removemoderator, addcontributor, removecontributor, editsettings, editflair, distinguish, marknsfw, - wikibanned, wikicontributor, wikiunbanned, wikipagelisted, removewikicontributor, wikirevise, wikipermlevel, - ignorereports, unignorereports, setpermissions, setsuggestedsort, sticky, unsticky, setcontestmode, unsetcontestmode, - lock, unlock, muteuser, unmuteuser, createrule, editrule, deleterule, spoiler, unspoiler` - * @returns {Promise} A Listing containing moderation actions - * @example - * - * r.getSubreddit('snoowrap').getModerationLog().then(console.log) - * - * // => Listing [ - * // ModAction { description: null, mod: 'snoowrap_testing', action: 'editflair', ... } - * // ModAction { description: null, mod: 'snoowrap_testing', action: 'approvecomment', ... } - * // ModAction { description: null, mod: 'snoowrap_testing', action: 'createrule', ... } - * // ] - */ - getModerationLog (options = {}) { - const parsedOptions = omit({...options, mod: options.mods && options.mods.join(',')}, 'mods'); - return this._getListing({uri: `r/${this.display_name}/about/log`, qs: parsedOptions}); - } - /** - * @summary Gets a list of reported items on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string} [options.only] Restricts the Listing to the specified type of item. One of `links, comments` - * @returns {Promise} A Listing containing reported items - * @example - * - * r.getSubreddit('snoowrap').getReports().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getReports (options = {}) { - return this._getListing({uri: `r/${this.display_name}/about/reports`, qs: options}); - } - /** - * @summary Gets a list of removed items on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string} [options.only] Restricts the Listing to the specified type of item. One of `links, comments` - * @returns {Promise} A Listing containing removed items - * @example - * - * r.getSubreddit('snoowrap').getSpam().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getSpam (options = {}) { - return this._getListing({uri: `r/${this.display_name}/about/spam`, qs: options}); - } - /** - * @summary Gets a list of items on the modqueue on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string} [options.only] Restricts the Listing to the specified type of item. One of `links, comments` - * @returns {Promise} A Listing containing items on the modqueue - * @example - * - * r.getSubreddit('snoowrap').getModqueue().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getModqueue (options = {}) { - return this._getListing({uri: `r/${this.display_name}/about/modqueue`, qs: options}); - } - /** - * @summary Gets a list of unmoderated items on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string} [options.only] Restricts the Listing to the specified type of item. One of `links, comments` - * @returns {Promise} A Listing containing unmoderated items - * @example - * - * r.getSubreddit('snoowrap').getUnmoderated().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getUnmoderated (options = {}) { - return this._getListing({uri: `r/${this.display_name}/about/unmoderated`, qs: options}); - } - /** - * @summary Gets a list of edited items on this subreddit. - * @param {object} [options={}] Options for the resulting Listing - * @param {string} [options.only] Restricts the Listing to the specified type of item. One of `links, comments` - * @returns {Promise} A Listing containing edited items - * @example - * - * r.getSubreddit('snoowrap').getEdited().then(console.log) - * // => Listing [ - * // Comment { ... }, - * // Comment { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getEdited (options = {}) { - return this._getListing({uri: `r/${this.display_name}/about/edited`, qs: options}); - } - /** - * @summary Accepts an invite to become a moderator of this subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').acceptModeratorInvite() - */ - acceptModeratorInvite () { - return this._post({ - uri: `r/${this.display_name}/api/accept_moderator_invite`, - form: {api_type} - }).then(handleJsonErrors(this)); - } - /** - * @summary Abdicates moderator status on this subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').leaveModerator() - */ - leaveModerator () { - return this.fetch().get('name').then(name => { - return this._post({uri: 'api/leavemoderator', form: {id: name}}).then(handleJsonErrors(this)); - }); - } - /** - * @summary Abdicates approved submitter status on this subreddit. - * @returns {Promise} A Promise that resolves with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').leaveContributor() - */ - leaveContributor () { - return this.fetch().get('name').then(name => this._post({uri: 'api/leavecontributor', form: {id: name}}).return(this)); - } - /** - * @summary Gets a subreddit's CSS stylesheet. - * @desc **Note**: This function will not work when snoowrap is running in a browser, because the reddit server sends a - redirect which cannot be followed by a CORS request. - * @desc **Note**: This method will return a 404 error if the subreddit in question does not have a custom stylesheet. - * @returns {Promise} A Promise for a string containing the subreddit's CSS. - * @example - * - * r.getSubreddit('snoowrap').getStylesheet().then(console.log) - * // => '.md blockquote,.md del,body{color:#121212}.usertext-body ... ' - */ - getStylesheet () { - return this._get({uri: `r/${this.display_name}/stylesheet`, json: false}); - } - /** - * @summary Conducts a search of reddit submissions, restricted to this subreddit. - * @param {object} options Search options. Can also contain options for the resulting Listing. - * @param {string} options.query The search query - * @param {string} [options.time] Describes the timespan that posts should be retrieved frome. One of - `hour, day, week, month, year, all` - * @param {string} [options.sort] Determines how the results should be sorted. One of `relevance, hot, top, new, comments` - * @param {string} [options.syntax='plain'] Specifies a syntax for the search. One of `cloudsearch, lucene, plain` - * @returns {Promise} A Listing containing the search results. - * @example - * - * r.getSubreddit('snoowrap').search({query: 'blah', sort: 'year'}).then(console.log) - * // => Listing [ - * // Submission { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - search (options) { - return this._r.search({...options, subreddit: this, restrictSr: true}); - } - /** - * @summary Gets the list of banned users on this subreddit. - * @param {object} options Filtering options. Can also contain options for the resulting Listing. - * @param {string} options.name A username on the list to jump to. - * @returns {Promise} A Listing of users - * @example - * - * r.getSubreddit('snoowrap').getBannedUsers().then(console.log) - * // => Listing [ - * // { date: 1461720936, note: '', name: 'actually_an_aardvark', id: 't2_q3519' } - * // ... - * // ] - * - */ - getBannedUsers (options) { - return this._getListing({uri: `r/${this.display_name}/about/banned`, qs: renameKey(options, 'name', 'user')}); - } - /** - * @summary Gets the list of muted users on this subreddit. - * @param {object} options Filtering options. Can also contain options for the resulting Listing. - * @param {string} options.name A username on the list to jump to. - * @returns {Promise} A Listing of users - * @example - * - * r.getSubreddit('snoowrap').getBannedUsers().then(console.log) - * // => Listing [ - * // { date: 1461720936, name: 'actually_an_aardvark', id: 't2_q3519' } - * // ... - * // ] - */ - getMutedUsers (options) { - return this._getListing({uri: `r/${this.display_name}/about/muted`, qs: renameKey(options, 'name', 'user')}); - } - /** - * @summary Gets the list of users banned from this subreddit's wiki. - * @param {object} options Filtering options. Can also contain options for the resulting Listing. - * @param {string} options.name A username on the list to jump to. - * @returns {Promise} A Listing of users - * @example - * - * r.getSubreddit('snoowrap').getWikibannedUsers().then(console.log) - * // => Listing [ - * // { date: 1461720936, note: '', name: 'actually_an_aardvark', id: 't2_q3519' } - * // ... - * // ] - */ - getWikibannedUsers (options) { - return this._getListing({uri: `r/${this.display_name}/about/wikibanned`, qs: renameKey(options, 'name', 'user')}); - } - /** - * @summary Gets the list of approved submitters on this subreddit. - * @param {object} options Filtering options. Can also contain options for the resulting Listing. - * @param {string} options.name A username on the list to jump to. - * @returns {Promise} A Listing of users - * @example - * - * r.getSubreddit('snoowrap').getContributors().then(console.log) - * // => Listing [ - * // { date: 1461720936, name: 'actually_an_aardvark', id: 't2_q3519' } - * // ... - * // ] - */ - getContributors (options) { - return this._getListing({uri: `r/${this.display_name}/about/contributors`, qs: renameKey(options, 'name', 'user')}); - } - /** - * @summary Gets the list of approved wiki submitters on this subreddit . - * @param {object} options Filtering options. Can also contain options for the resulting Listing. - * @param {string} options.name A username on the list to jump to. - * @returns {Promise} A Listing of users - * @example - * - * r.getSubreddit('snoowrap').getWikiContributors().then(console.log) - * // => Listing [ - * // { date: 1461720936, name: 'actually_an_aardvark', id: 't2_q3519' } - * // ... - * // ] - */ - getWikiContributors (options) { - return this._getListing({uri: `r/${this.display_name}/about/wikicontributors`, qs: renameKey(options, 'name', 'user')}); - } - /** - * @summary Gets the list of moderators on this subreddit. - * @param {object} options - * @param {string} [options.name] The name of a user to find in the list - * @returns {Promise} An Array of RedditUsers representing the moderators of this subreddit - * @example - * - * r.getSubreddit('AskReddit').getModerators().then(console.log) - * // => [ - * // RedditUser { date: 1453862639, mod_permissions: [ 'all' ], name: 'not_an_aardvark', id: 't2_k83md' }, - * // ... - * // ] - * - */ - getModerators ({name} = {}) { - return this._get({uri: `r/${this.display_name}/about/moderators`, qs: {user: name}}); - } - /** - * @summary Deletes the banner for this Subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteBanner() - */ - deleteBanner () { - return this._post({uri: `r/${this.display_name}/api/delete_sr_banner`, form: {api_type}}).then(handleJsonErrors(this)); - } - /** - * @summary Deletes the header image for this Subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteHeader() - */ - deleteHeader () { - return this._post({uri: `r/${this.display_name}/api/delete_sr_header`, form: {api_type}}).then(handleJsonErrors(this)); - } - /** - * @summary Deletes this subreddit's icon. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteIcon() - */ - deleteIcon () { - return this._post({uri: `r/${this.display_name}/api/delete_sr_icon`, form: {api_type}}).then(handleJsonErrors(this)); - } - /** - * @summary Deletes an image from this subreddit. - * @param {object} options - * @param {string} options.imageName The name of the image. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').deleteImage() - */ - deleteImage ({image_name, imageName = image_name}) { - return this._post({ - uri: `r/${this.display_name}/api/delete_sr_img`, - form: {api_type, img_name: imageName} - }).then(handleJsonErrors(this)); - } - /** - * @summary Gets this subreddit's current settings. - * @returns {Promise} An Object containing this subreddit's current settings. - * @example - * - * r.getSubreddit('snoowrap').getSettings().then(console.log) - * // => SubredditSettings { default_set: true, submit_text: '', subreddit_type: 'private', ... } - */ - getSettings () { - return this._get({uri: `r/${this.display_name}/about/edit`}); - } - /** - * @summary Edits this subreddit's settings. - * @param {object} options An Object containing {[option name]: new value} mappings of the options that should be modified. - Any omitted option names will simply retain their previous values. - * @param {string} options.title The text that should appear in the header of the subreddit - * @param {string} options.public_description The text that appears with this Subreddit on the search page, or on the - blocked-access page if this subreddit is private. (500 characters max) - * @param {string} options.description The sidebar text for the subreddit. (5120 characters max) - * @param {string} [options.submit_text=''] The text to show below the submission page (1024 characters max) - * @param {boolean} [options.hide_ads=false] Determines whether ads should be hidden on this subreddit. (This is only - allowed for gold-only subreddits.) - * @param {string} [options.lang='en'] The language of the subreddit (represented as an IETF language tag) - * @param {string} [options.type='public'] Determines who should be able to access the subreddit. This should be one of - `public, private, restricted, gold_restricted, gold_only, archived, employees_only`. - * @param {string} [options.link_type='any'] Determines what types of submissions are allowed on the subreddit. This should - be one of `any, link, self`. - * @param {string} [options.submit_link_label=undefined] Custom text to display on the button that submits a link. If - this is omitted, the default text will be displayed. - * @param {string} [options.submit_text_label=undefined] Custom text to display on the button that submits a selfpost. If - this is omitted, the default text will be displayed. - * @param {string} [options.wikimode='modonly'] Determines who can edit wiki pages on the subreddit. This should be one of - `modonly, anyone, disabled`. - * @param {number} [options.wiki_edit_karma=0] The minimum amount of subreddit karma needed for someone to edit this - subreddit's wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) - * @param {number} [options.wiki_edit_age=0] The minimum account age (in days) needed for someone to edit this subreddit's - wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) - * @param {string} [options.spam_links='high'] The spam filter strength for links on this subreddit. This should be one of - `low, high, all`. - * @param {string} [options.spam_selfposts='high'] The spam filter strength for selfposts on this subreddit. This should be - one of `low, high, all`. - * @param {string} [options.spam_comments='high'] The spam filter strength for comments on this subreddit. This should be one - of `low, high, all`. - * @param {boolean} [options.over_18=false] Determines whether this subreddit should be classified as NSFW - * @param {boolean} [options.allow_top=true] Determines whether the new subreddit should be able to appear in /r/all and - trending subreddits - * @param {boolean} [options.show_media=false] Determines whether image thumbnails should be enabled on this subreddit - * @param {boolean} [options.show_media_preview=true] Determines whether media previews should be expanded by default on this - subreddit - * @param {boolean} [options.allow_images=true] Determines whether image uploads and links to image hosting sites should be - enabled on this subreddit - * @param {boolean} [options.exclude_banned_modqueue=false] Determines whether posts by site-wide banned users should be - excluded from the modqueue. - * @param {boolean} [options.public_traffic=false] Determines whether the /about/traffic page for this subreddit should be - viewable by anyone. - * @param {boolean} [options.collapse_deleted_comments=false] Determines whether deleted and removed comments should be - collapsed by default - * @param {string} [options.suggested_comment_sort=undefined] The suggested comment sort for the subreddit. This should be - one of `confidence, top, new, controversial, old, random, qa`.If left blank, there will be no suggested sort, - which means that users will see the sort method that is set in their own preferences (usually `confidence`.) - * @param {boolean} [options.spoilers_enabled=false] Determines whether users can mark their posts as spoilers - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').editSettings({submit_text: 'Welcome! Please be sure to read the rules.'}) - */ - editSettings (options) { - return Promise.join(this.getSettings(), this.fetch().get('name'), (currentValues, name) => { - return this._r._createOrEditSubreddit({ - ...renameKey(currentValues, 'subreddit_type', 'type'), - ...options, - sr: name - }); - }).return(this); - } - /** - * @summary Gets a list of recommended other subreddits given this one. - * @param {object} [options] - * @param {Array} [options.omit=[]] An Array of subreddit names that should be excluded from the listing. - * @returns {Promise} An Array of subreddit names - * @example - * - * r.getSubreddit('AskReddit').getRecommendedSubreddits().then(console.log); - * // [ 'TheChurchOfRogers', 'Sleepycabin', ... ] - */ - getRecommendedSubreddits (options) { - const toOmit = options.omit && options.omit.join(','); - return this._get({uri: `api/recommend/sr/${this.display_name}`, qs: {omit: toOmit}}).then(names => map(names, 'sr_name')); - } - /** - * @summary Gets the submit text (which displays on the submission form) for this subreddit. - * @returns {Promise} The submit text, represented as a string. - * @example - * - * r.getSubreddit('snoowrap').getSubmitText().then(console.log) - * // => 'Welcome! Please be sure to read the rules.' - */ - getSubmitText () { - return this._get({uri: `r/${this.display_name}/api/submit_text`}).get('submit_text'); - } - /** - * @summary Updates this subreddit's stylesheet. - * @param {object} options - * @param {string} options.css The new contents of the stylesheet - * @param {string} [options.reason] The reason for the change (256 characters max) - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').updateStylesheet({css: 'body {color:#00ff00;}', reason: 'yay green'}) - */ - updateStylesheet ({css, reason}) { - return this._post({ - uri: `r/${this.display_name}/api/subreddit_stylesheet`, - form: {api_type, op: 'save', reason, stylesheet_contents: css} - }).then(handleJsonErrors(this)); - } - - _setSubscribed (status) { - return this._post({ - uri: 'api/subscribe', - form: {action: status ? 'sub' : 'unsub', sr_name: this.display_name} - }).return(this); - } - /** - * @summary Subscribes to this subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').subscribe() - */ - subscribe () { - return this._setSubscribed(true); - } - /** - * @summary Unsubscribes from this subreddit. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').unsubscribe() - */ - unsubscribe () { - /* Reddit returns a 404 error if the user attempts to unsubscribe to a subreddit that they weren't subscribed to in the - first place. It also (as one would expect) returns a 404 error if the subreddit in question does not exist. snoowrap - should swallow the first type of error internally, but it should raise the second type of error. Unfortunately, the errors - themselves are indistinguishable. So if a 404 error gets thrown, fetch the current subreddit to check if it exists. If it - does exist, then the 404 error was of the first type, so swallow it and return the current Subreddit object as usual. If - the subreddit doesn't exist, then the original error was of the second type, so throw it. */ - return this._setSubscribed(false).catch({statusCode: 404}, err => this.fetch().return(this).catchThrow(err)); - } - _uploadSrImg ({name, file, uploadType, imageType}) { - if (typeof file !== 'string' && !(file instanceof Readable)) { - throw new InvalidMethodCallError('Uploaded image filepath must be a string or a ReadableStream.'); - } - const parsedFile = typeof file === 'string' ? createReadStream(file) : file; - return this._post({ - uri: `r/${this.display_name}/api/upload_sr_img`, - formData: {name, upload_type: uploadType, img_type: imageType, file: parsedFile} - }).then(result => { - if (result.errors.length) { - throw result.errors[0]; - } - return this; - }); - } - /** - * @summary Uploads an image for use in this subreddit's stylesheet. - * @param {object} options - * @param {string} options.name The name that the new image should have in the stylesheet - * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an - image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) in environments (e.g. - browsers) where the filesystem is unavailable. - * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').uploadSubredditImage({name: 'the cookie monster', file: './cookie_monster.png'}) - */ - uploadStylesheetImage ({name, file, image_type = 'png', imageType = image_type}) { - return this._uploadSrImg({name, file, imageType, uploadType: 'img'}); - } - /** - * @summary Uploads an image to use as this subreddit's header. - * @param {object} options - * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an - image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) for environments (e.g. - browsers) where the filesystem is unavailable. - * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').uploadHeaderImage({name: 'the cookie monster', file: './cookie_monster.png'}) - */ - uploadHeaderImage ({file, image_type = 'png', imageType = image_type}) { - return this._uploadSrImg({file, imageType, uploadType: 'header'}); - } - /** - * @summary Uploads an image to use as this subreddit's mobile icon. - * @param {object} options - * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an - image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) for environments (e.g. - browsers) where the filesystem is unavailable. - * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').uploadIcon({name: 'the cookie monster', file: './cookie_monster.png'}) - */ - uploadIcon ({file, image_type = 'png', imageType = image_type}) { - return this._uploadSrImg({file, imageType, uploadType: 'icon'}); - } - /** - * @summary Uploads an image to use as this subreddit's mobile banner. - * @param {object} options - * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an - image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) for environments (e.g. - browsers) where the filesystem is unavailable. - * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete. - * @example r.getSubreddit('snoowrap').uploadBannerImage({name: 'the cookie monster', file: './cookie_monster.png'}) - */ - uploadBannerImage ({file, image_type = 'png', imageType = image_type}) { - return this._uploadSrImg({file, imageType, upload_type: 'banner'}); - } - /** - * @summary Gets information on this subreddit's rules. - * @returns {Promise} A Promise that fulfills with information on this subreddit's rules. - * @example - * - * r.getSubreddit('snoowrap').getRules().then(console.log) - * - * // => { - * rules: [ - * { - * kind: 'all', - * short_name: 'Rule 1: No violating rule 1', - * description: 'Breaking this rule is not allowed.', - * ... - * }, - * ... - * ], - * site_rules: [ - * 'Spam', - * 'Personal and confidential information'', - * 'Threatening, harassing, or inciting violence' - * ] - * } - */ - getRules () { - return this._get({uri: `r/${this.display_name}/about/rules`}); - } - /** - * @summary Gets the stickied post on this subreddit, or throws a 404 error if none exists. - * @param {object} [options] - * @param {number} [options.num=1] The number of the sticky to get. Should be either `1` (first sticky) or `2` (second sticky). - * @returns {Promise} A Submission object representing this subreddit's stickied submission - * @example - * r.getSubreddit('snoowrap').getSticky({num: 2}) - * // => Submission { ... } - */ - getSticky ({num = 1} = {}) { - return this._get({uri: `r/${this.display_name}/about/sticky`, qs: {num}}); - } - _friend (options) { - return this._post({ - uri: `r/${this.display_name}/api/friend`, - form: {...options, api_type} - }).then(handleJsonErrors(this)); - } - _unfriend (options) { - return this._post({ - uri: `r/${this.display_name}/api/unfriend`, - form: {...options, api_type} - }).then(handleJsonErrors(this)); - } - /** - * @summary Invites the given user to be a moderator of this subreddit. - * @param {object} options - * @param {string} options.name The username of the account that should be invited - * @param {Array} [options.permissions] The moderator permissions that this user should have. This should be an array - containing some combination of `"wiki", "posts", "access", "mail", "config", "flair"`. To add a moderator with full - permissions, omit this property entirely. - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').inviteModerator({name: 'actually_an_aardvark', permissions: ['posts', 'wiki']}) - */ - inviteModerator ({name, permissions}) { - return this._friend({name, permissions: formatModPermissions(permissions), type: 'moderator_invite'}); - } - /** - * @summary Revokes an invitation for the given user to be a moderator. - * @param {object} options - * @param {string} options.name The username of the account whose invitation should be revoked - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').revokeModeratorInvite({name: 'actually_an_aardvark'}) - */ - revokeModeratorInvite ({name}) { - return this._unfriend({name, type: 'moderator_invite'}); - } - /** - * @summary Removes the given user's moderator status on this subreddit. - * @param {object} options - * @param {string} options.name The username of the account whose moderator status should be removed - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').removeModerator({name: 'actually_an_aardvark'}) - */ - removeModerator ({name}) { - return this._unfriend({name, type: 'moderator'}); - } - /** - * @summary Makes the given user an approved submitter of this subreddit. - * @param {object} options - * @param {string} options.name The username of the account that should be given this status - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').addContributor({name: 'actually_an_aardvark'}) - */ - addContributor ({name}) { - return this._friend({name, type: 'contributor'}); - } - /** - * @summary Revokes this user's approved submitter status on this subreddit. - * @param {object} options - * @param {string} options.name The username of the account whose status should be revoked - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').removeContributor({name: 'actually_an_aardvark'}) - */ - removeContributor ({name}) { - return this._unfriend({name, type: 'contributor'}); - } - /** - * @summary Bans the given user from this subreddit. - * @param {object} options - * @param {string} options.name The username of the account that should be banned - * @param {string} [options.banMessage] The ban message. This will get sent to the user in a private message, alerting them - that they have been banned. - * @param {string} [options.banReason] A string indicating which rule the banned user broke (100 characters max) - * @param {number} [options.duration] The duration of the ban, in days. For a permanent ban, omit this parameter. - * @param {string} [options.banNote] A note that appears on the moderation log, usually used to indicate the reason for the - ban. This is not visible to the banned user. (300 characters max) - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').banUser({name: 'actually_an_aardvark', banMessage: 'You are now banned LOL'}) - */ - banUser ({ - name, - ban_message, banMessage = ban_message, - ban_reason, banReason = ban_reason, - duration, - ban_note, banNote = ban_note - }) { - return this._friend({ - name, ban_message: banMessage, - ban_reason: banReason, - duration, - note: banNote, - type: 'banned' - }); - } - /** - * @summary Unbans the given user from this subreddit. - * @param {object} options - * @param {string} options.name The username of the account that should be unbanned - * @returns {Promise} A Promise that fulfills when the request is complete - * @example r.getSubreddit('snoowrap').unbanUser({name: 'actually_an_aardvark'}) - */ - unbanUser ({name}) { - return this._unfriend({name, type: 'banned'}); - } - /** - * @summary Mutes the given user from messaging this subreddit for 72 hours. - * @param {object} options - * @param {string} options.name The username of the account that should be muted - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').muteUser({name: 'actually_an_aardvark'}) - */ - muteUser ({name}) { - return this._friend({name, type: 'muted'}); - } - /** - * @summary Unmutes the given user from messaging this subreddit. - * @param {object} options - * @param {string} options.name The username of the account that should be muted - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').unmuteUser({name: 'actually_an_aardvark'}) - */ - unmuteUser ({name}) { - return this._unfriend({name, type: 'muted'}); - } - /** - * @summary Bans the given user from editing this subreddit's wiki. - * @param {object} options - * @param {string} options.name The username of the account that should be wikibanned - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').wikibanUser({name: 'actually_an_aardvark'}) - */ - wikibanUser ({name}) { - return this._friend({name, type: 'wikibanned'}); - } - /** - * @summary Unbans the given user from editing this subreddit's wiki. - * @param {object} options - * @param {string} options.name The username of the account that should be unwikibanned - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').unwikibanUser({name: 'actually_an_aardvark'}) - */ - unwikibanUser ({name}) { - return this._unfriend({name, type: 'wikibanned'}); - } - /** - * @summary Adds the given user to this subreddit's list of approved wiki editors. - * @param {object} options - * @param {string} options.name The username of the account that should be given approved editor status - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').addWikiContributor({name: 'actually_an_aardvark'}) - */ - addWikiContributor ({name}) { - return this._friend({name, type: 'wikicontributor'}); - } - /** - * @summary Removes the given user from this subreddit's list of approved wiki editors. - * @param {object} options - * @param {string} options.name The username of the account whose approved editor status should be revoked - * @returns {Promise} A Promise that fulfills with this Subreddit when the request is complete - * @example r.getSubreddit('snoowrap').removeWikiContributor({name: 'actually_an_aardvark'}) - */ - removeWikiContributor ({name}) { - return this._unfriend({name, type: 'wikicontributor'}); - } - /** - * @summary Sets the permissions for a given moderator on this subreddit. - * @param {object} options - * @param {string} options.name The username of the moderator whose permissions are being changed - * @param {Array} [options.permissions] The new moderator permissions that this user should have. This should be an array - containing some combination of `"wiki", "posts", "access", "mail", "config", "flair"`. To add a moderator with full - permissions, omit this property entirely. - * @returns {Promise} A Promise that fulfills with this Subreddit when this request is complete - * @example r.getSubreddit('snoowrap').setModeratorPermissions({name: 'actually_an_aardvark', permissions: ['mail']}) - */ - setModeratorPermissions ({name, permissions}) { - return this._post({ - uri: `r/${this.display_name}/api/setpermissions`, - form: {api_type, name, permissions: formatModPermissions(permissions), type: 'moderator'} - }).then(handleJsonErrors(this)); - } - /** - * @summary Gets a given wiki page on this subreddit. - * @param {string} title The title of the desired wiki page. - * @returns {WikiPage} An unfetched WikiPage object corresponding to the desired wiki page - * @example - * - * r.getSubreddit('snoowrap').getWikiPage('index') - * // => WikiPage { title: 'index', subreddit: Subreddit { display_name: 'snoowrap' } } - */ - getWikiPage (title) { - return this._r._newObject('WikiPage', {subreddit: this, title}); - } - /** - * @summary Gets the list of wiki pages on this subreddit. - * @returns {Promise} An Array containing WikiPage objects - * @example - * - * r.getSubreddit('snoowrap').getWikiPages().then(console.log) - * // => [ - * // WikiPage { title: 'index', subreddit: Subreddit { display_name: 'snoowrap'} } - * // WikiPage { title: 'config/sidebar', subreddit: Subreddit { display_name: 'snoowrap'} } - * // WikiPage { title: 'secret_things', subreddit: Subreddit { display_name: 'snoowrap'} } - * // WikiPage { title: 'config/submit_text', subreddit: Subreddit { display_name: 'snoowrap'} } - * // ] - */ - getWikiPages () { - return this._get({uri: `r/${this.display_name}/wiki/pages`}).map(title => this.getWikiPage(title)); - } - /** - * @summary Gets a list of revisions on this subreddit's wiki. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing wiki revisions - * @example - * - * r.getSubreddit('snoowrap').getWikiRevisions().then(console.log) - * // => Listing [ - * // { page: 'index', reason: 'added cookies', ... }, - * // ... - * // ] - */ - getWikiRevisions (options) { - return this._getListing({uri: `r/${this.display_name}/wiki/revisions`, qs: options}); - } -}; - -export default Subreddit; diff --git a/src/objects/Subreddit.ts b/src/objects/Subreddit.ts new file mode 100644 index 00000000..4db838ff --- /dev/null +++ b/src/objects/Subreddit.ts @@ -0,0 +1,1590 @@ +import {chunk, flatten, map} from 'lodash' +import stream from 'stream' +import fs from 'fs' +import {formatModPermissions, handleJsonErrors, renameKey} from '../helper' +import {InvalidMethodCallError} from '../errors' +import RedditContent from './RedditContent' +import type { + CreateFlairOptions, FlairCSVOptions, FlairSelectorOptions, OmitProps, RichTextFlair, SelectFlairOptions, SubmitLinkOptions, + SubmitImageOptions, SubmitVideoOptions, SubmitGalleryOptions, SubmitSelfpostOptions, SubmitPollOptions, + SubmitCrosspostOptions, + SubredditOptions +} from '../interfaces' +import type {ListingQuery} from './Listing' +import type {SearchOptions} from '../snoowrap' +import type {AxiosError} from '../axiosCreate' +import type {COMMENT_SORTS} from '../constants' +// +const api_type = 'json' + + +interface FlairListingQuery extends ListingQuery { + /** A specific username to jump to. */ + name?: string +} + +interface FlairSettings { + /** Determines whether user flair should be enabled. */ + flair_enabled: boolean + /** Determines the orientation of user flair relative to a given username. */ + flair_position: 'left'|'right' + /** Determines whether users should be able to edit their own flair. */ + flair_self_assign_enabled: boolean + /** Determines the orientation of link flair relative to a link title. */ + link_flair_position: 'left'|'right' + /** Determines whether users should be able to edit the flair of their. */ + link_flair_self_assign_enabled: boolean + [key: string]: any +} + +interface ModerationLogQuery extends ListingQuery { + /** An array of moderator names that the results should be restricted to. */ + mods?: string[] + /** A string of comma-separated moderator names. */ + mod?: string + /** Restricts the results to the specified type. */ + type?: 'banuser'|'unbanuser'|'removelink'|'approvelink'|'removecomment'|'approvecomment' + |'addmoderator'|'invitemoderator'|'uninvitemoderator'|'acceptmoderatorinvite'|'removemoderator' + |'addcontributor'|'removecontributor'|'editsettings'|'editflair'|'distinguish'|'marknsfw' + |'wikibanned'|'wikicontributor'|'wikiunbanned'|'wikipagelisted'|'removewikicontributor' + |'wikirevise'|'wikipermlevel'|'ignorereports'|'unignorereports'|'setpermissions' + |'setsuggestedsort'|'sticky'|'unsticky'|'setcontestmode'|'unsetcontestmode'|'lock'|'unlock' + |'muteuser'|'unmuteuser'|'createrule'|'editrule'|'deleterule'|'spoiler'|'unspoiler' +} + +interface FilterableListingQuery extends ListingQuery { + /** Restricts the Listing to the specified type of item. */ + only?: 'links'|'comments' +} + +interface UserListingQuery extends ListingQuery { + /** A username on the list to jump to. */ + user?: string + /** Expand subreddits. */ + sr_detail?: boolean +} + +interface Subreddit { + accounts_active_is_fuzzed: boolean + accounts_active: number + active_user_count: number + advertiser_category: string|null + all_original_content: boolean + allow_discovery: boolean + allow_images: boolean + allow_videogifs: boolean + allow_videos: boolean + /** HEX color code */ + banner_background_color: string + /** URL of the banner image used on desktop Reddit */ + banner_background_image: string + /** URL of the banner image used on the mobile Reddit app */ + banner_img: string + banner_size: [number, number]|null + can_assign_link_flair: boolean + can_assign_user_flair: boolean + collapse_deleted_comments: boolean + comment_score_hide_mins: number + /** Image URL of the subreddit icon */ + community_icon: string + created_utc: number + created: number + description_html: string + description: string + display_name: string + display_name_prefixed: string + emojis_custom_size: [number, number]|null + emojis_enabled: boolean + has_menu_widget: boolean + header_img: string|null + header_size: [number, number]|null + header_title: string|null + hide_ads: boolean + id: string + icon_img: string + icon_size: [number, number]|null + is_enrolled_in_new_modmail: boolean|null + key_color: string + lang: string + link_flair_enabled: boolean + link_flair_position: ''|'left'|'right' + name: string + /** Will be null if user is not subscribed to this subreddit */ + notification_level: string|null + over18: boolean + /** HEX color code */ + primary_color: string + public_description_html: string + public_description: string + public_traffic: boolean + quarantine: boolean + show_media_preview: boolean + show_media: boolean + spoilers_enabled: boolean + submission_type: 'any'|'link'|'self' + submit_link_label: string|null + submit_text_html: string + submit_text_label: string|null + submit_text: string + subreddit_type: 'public'|'private'|'restricted'|'gold_restricted'|'gold_only'|'archived'|'employees_only' + subscribers: number + suggested_comment_sort: typeof COMMENT_SORTS[number]|null + title: string + url: string + user_can_flair_in_sr: boolean + user_flair_background_color: string|null + user_flair_css_class: string|null + user_flair_enabled_in_sr: boolean + user_flair_position: ''|'left'|'right' + user_flair_richtext: RichTextFlair[] + user_flair_template_id: string|null + user_flair_text: string|null + user_flair_text_color: 'dark'|'light'|null + user_has_favorited: boolean + user_is_banned: boolean + user_is_contributor: boolean + user_is_moderator: boolean + user_is_muted: boolean + user_is_subscriber: boolean + user_sr_flair_enabled: boolean + user_sr_theme_enabled: boolean + whitelist_status: string + wiki_enabled: boolean + wls: number +} + +/** + * A class representing a subreddit + * @example + * + * // Get a subreddit by name + * r.getSubreddit('AskReddit') + */ +class Subreddit extends RedditContent { + static _name = 'Subreddit' + + get _uri () { + return `r/${this.display_name}/about` + } + _transformApiResponse (response: Subreddit) { + if (!(response instanceof Subreddit)) { + throw new TypeError(`The subreddit /r/${this.display_name} does not exist.`) + } + return response + } + async _deleteFlairTemplates (flair_type: string) { + await this._post({url: `r/${this.display_name}/api/clearflairtemplates`, form: {api_type, flair_type}}) + return this + } + /** + * @summary Deletes all of this subreddit's user flair templates + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteAllUserFlairTemplates() + */ + deleteAllUserFlairTemplates () { + return this._deleteFlairTemplates('USER_FLAIR') + } + /** + * @summary Deletes all of this subreddit's link flair templates + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteAllLinkFlairTemplates() + */ + deleteAllLinkFlairTemplates () { + return this._deleteFlairTemplates('LINK_FLAIR') + } + /** + * @summary Deletes one of this subreddit's flair templates + * @param {object} options + * @param {string} options.flair_template_id The ID of the template that should be deleted + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteFlairTemplate({flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721'}) + */ + async deleteFlairTemplate (flair_template_id: string) { + await this._post({ + url: `r/${this.display_name}/api/deleteflairtemplate`, + form: {api_type, flair_template_id} + }) + return this + } + async _createFlairTemplate ({ + text, + css_class, + flair_type, + text_editable = false, + ...opts + }: CreateFlairOptions) { + await this._post({ + url: `r/${this.display_name}/api/flairtemplate`, + form: {...opts, api_type, text, css_class, flair_type, text_editable} + }) + return this + } + /** + * @summary Creates a new user flair template for this subreddit + * @param {object} options + * @returns A Promise that fulfills with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').createUserFlairTemplate({text: 'Some Flair Text', cssClass: 'some-css-class'}) + */ + createUserFlairTemplate (options: OmitProps) { + return this._createFlairTemplate({...options, flair_type: 'USER_FLAIR'}) + } + /** + * @summary Creates a new link flair template for this subreddit + * @param {object} options + * @returns A Promise that fulfills with this Subredit when the request is complete. + * @example r.getSubreddit('snoowrap').createLinkFlairTemplate({text: 'Some Flair Text', cssClass: 'some-css-class'}) + */ + createLinkFlairTemplate (options: OmitProps) { + return this._createFlairTemplate({...options, flair_type: 'LINK_FLAIR'}) + } + _getFlairOptions ({name, link, is_newlink}: FlairSelectorOptions = {}) { // TODO: Add shortcuts for this on RedditUser and Submission + return this._post({url: `r/${this.display_name}/api/flairselector`, form: {name, link, is_newlink}}) + } + /** + * @summary Gets the flair templates for the subreddit or a given link. + * @param {string} [linkId] The link's base36 ID + * @returns An Array of flair template options + * @example + * + * r.getSubreddit('snoowrap').getLinkFlairTemplates('4fp36y').then(console.log) + * // => [ { flair_css_class: '', + * // flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721', + * // flair_text_editable: true, + * // flair_position: 'right', + * // flair_text: '' }, + * // { flair_css_class: '', + * // flair_template_id: '03821f62-c920-11e5-b608-0e309fbcf863', + * // flair_text_editable: true, + * // flair_position: 'right', + * // flair_text: '' }, + * // ... + * // ] + */ + async getLinkFlairTemplates (link: string) { + const options = link ? {link} : {is_newlink: true} + const res = await this._getFlairOptions(options) + return res.choices + } + /** + * @summary Gets the list of user flair templates on this subreddit. + * @returns An Array of user flair templates + * @example + * + * r.getSubreddit('snoowrap').getUserFlairTemplates().then(console.log) + * // => [ { flair_css_class: '', + * // flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721', + * // flair_text_editable: true, + * // flair_position: 'right', + * // flair_text: '' }, + * // { flair_css_class: '', + * // flair_template_id: '03821f62-c920-11e5-b608-0e309fbcf863', + * // flair_text_editable: true, + * // flair_position: 'right', + * // flair_text: '' }, + * // ... + * // ] + */ + async getUserFlairTemplates () { + const res = await this._getFlairOptions() + return res.choices + } + /** + * @summary Clears a user's flair on this subreddit. + * @param name The user's name + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteUserFlair('actually_an_aardvark') + */ + async deleteUserFlair (name: string) { + await this._post({url: `r/${this.display_name}/api/deleteflair`, form: {api_type, name}}) + return this + } + /** + * @summary Gets a user's flair on this subreddit. + * @param name The user's name + * @returns An object representing the user's flair + * @example + * + * r.getSubreddit('snoowrap').getUserFlair('actually_an_aardvark').then(console.log) + * // => { flair_css_class: '', + * // flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721', + * // flair_text: '', + * // flair_position: 'right' + * // } + */ + async getUserFlair (name: string) { + const res = await this._getFlairOptions({name}) + return res.current + } + /** + * @summary Sets multiple user flairs at the same time + * @desc Due to the behavior of the reddit API endpoint that this function uses, if any of the provided user flairs are + * invalid, reddit will make note of this in its response, but it will still attempt to set the remaining user flairs. If this + * occurs, the Promise returned by snoowrap will be rejected, and the rejection reason will be an array containing the 'error' + * responses from reddit. + * @param {object[]} flairArray + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example + * r.getSubreddit('snoowrap').setMultipleUserFlairs([ + * {name: 'actually_an_aardvark', text: "this is /u/actually_an_aardvark's flair text", css_class: 'some-css-class'}, + * {name: 'snoowrap_testing', text: "this is /u/snoowrap_testing's flair text", css_class: 'some-css-class'} + * ]) + * // the above request gets completed successfully + * + * r.getSubreddit('snoowrap').setMultipleUserFlairs([ + * {name: 'actually_an_aardvark', text: 'foo', css_class: 'valid-css-class'}, + * {name: 'snoowrap_testing', text: 'bar', css_class: "this isn't a valid css class"}, + * {name: 'not_an_aardvark', text: 'baz', css_class: "this also isn't a valid css class"} + * ]) + * // the Promise from the above request gets rejected, with the following rejection reason: + * [ + * { + * status: 'skipped', + * errors: { css: 'invalid css class `this isn\'t a valid css class\', ignoring' }, + * ok: false, + * warnings: {} + * }, + * { + * status: 'skipped', + * errors: { css: 'invalid css class `this also isn\'t a valid css class\', ignoring' }, + * ok: false, + * warnings: {} + * } + * ] + * // note that /u/actually_an_aardvark's flair still got set by the request, even though the other two flairs caused errors. + */ + async setMultipleUserFlairs (flairArray: FlairCSVOptions) { + const csvLines = flairArray.map(item => { + // reddit expects to receive valid CSV data, which each line having the form `username,flair_text,css_class`. + return [ + item.name, + item.text || '', + item.css_class || '' + ].map(str => { + /** + * To escape special characters in the lines (e.g. if the flair text itself contains a comma), surround each + * part of the line with double quotes before joining the parts together with commas (in accordance with how special + * characters are usually escaped in CSV). If double quotes are themselves part of the flair text, replace them with a + * pair of consecutive double quotes. + */ + return `"${str.replace(/"/g, '""')}"` + }).join(',') + }) + /** + * Due to an API limitation, this endpoint can only set the flair of 100 users at a time. + * Send multiple requests if necessary to ensure that all users in the array are accounted for. + */ + const flairChunks = await Promise.all(chunk(csvLines, 100).map(flairChunk => { + return this._post({url: `r/${this.display_name}/api/flaircsv`, form: {flair_csv: flairChunk.join('\n')}}) + })) + const results = flatten(flairChunks) + const errorRows = results.filter(row => !row.ok) + if (errorRows.length) { + throw errorRows + } + return this + } + /** + * @summary Gets a list of all user flairs on this subreddit. + * @param {object} options + * @returns A Listing containing user flairs + * @example + * + * r.getSubreddit('snoowrap').getUserFlairList().then(console.log) + * // => Listing [ + * // { flair_css_class: null, + * // user: 'not_an_aardvark', + * // flair_text: 'Isn\'t an aardvark' }, + * // { flair_css_class: 'some-css-class', + * // user: 'actually_an_aardvark', + * // flair_text: 'this is /u/actually_an_aardvark\'s flair text' }, + * // { flair_css_class: 'some-css-class', + * // user: 'snoowrap_testing', + * // flair_text: 'this is /u/snoowrap_testing\'s flair text' } + * // ] + */ + getUserFlairList (options: FlairListingQuery = {}) { + return this._getListing({uri: `r/${this.display_name}/api/flairlist`, qs: options, _transform: response => { + /** + * For unknown reasons, responses from the api/flairlist endpoint are formatted differently than responses from all other + * Listing endpoints. Most Listing endpoints return an object with a `children` property containing the Listing's children, + * and `after` and `before` properties corresponding to the `after` and `before` querystring parameters that a client should + * use in the next request. However, the api/flairlist endpoint returns an object with a `users` property containing the + * Listing's children, and `next` and `prev` properties corresponding to the `after` and `before` querystring parameters. As + * far as I can tell, there's no actual reason for this difference. >_> + */ + response.after = response.next || null + response.before = response.prev || null + response.children = response.users + return this._r._newObject('Listing', response) + }}) + } + /** + * @summary Configures the flair settings for this subreddit. + * @param {object} options + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').configure_flair({ + * flair_enabled: true, + * flair_position: 'left', + * flair_self_assign_enabled: false, + * link_flair_position: 'right', + * link_flair_self_assign_enabled: false + * }) + */ + async configureFlair ({ + flair_enabled, + flair_position, + flair_self_assign_enabled, + link_flair_position, + link_flair_self_assign_enabled, + ...opts + }: FlairSettings) { + await this._post({url: `r/${this.display_name}/api/flairconfig`, form: { + ...opts, + api_type, + flair_enabled, + flair_position, + flair_self_assign_enabled, + link_flair_position, + link_flair_self_assign_enabled + }}) + return this + } + /** + * @summary Gets the requester's flair on this subreddit. + * @returns An object representing the requester's current flair + * @example + * + * r.getSubreddit('snoowrap').getMyFlair().then(console.log) + * // => { flair_css_class: 'some-css-class', + * // flair_template_id: null, + * // flair_text: 'this is /u/snoowrap_testing\'s flair text', + * // flair_position: 'right' + * // } + */ + async getMyFlair () { + return (await this._getFlairOptions()).current + } + /** + * @summary Sets the requester's flair on this subreddit. + * @param options + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').selectMyFlair({flair_template_id: 'fdfd8532-c91e-11e5-b4d4-0e082084d721'}) + */ + async selectMyFlair (options: OmitProps) { + /** + * NOTE: This requires `identity` scope in addition to `flair` scope, since the reddit api needs to be passed a username. + * I'm not sure if there's a way to do this without requiring additional scope. + */ + const name = await this._r._getMyName() + await this._r._selectFlair({...options, name, subredditName: this.display_name}) + return this + } + async _setMyFlairVisibility (flair_enabled: boolean) { + await this._post({url: `r/${this.display_name}/api/setflairenabled`, form: {api_type, flair_enabled}}) + return this + } + /** + * @summary Makes the requester's flair visible on this subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').showMyFlair() + */ + showMyFlair () { + return this._setMyFlairVisibility(true) + } + /** + * @summary Makes the requester's flair invisible on this subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').hideMyFlair() + */ + hideMyFlair () { + return this._setMyFlairVisibility(false) + } + /** + * @summary Creates a new link submission on this subreddit. + * @param {object} options An object containing details about the submission. + * @returns The newly-created Submission object. + * @example + * + * subreddit.submitLink({ + * title: 'I found a cool website!', + * url: 'https://google.com' + * }).then(console.log) + * // => Submission { name: 't3_4abnfe' } + * // (new linkpost created on reddit) + */ + submitLink (options: OmitProps) { + return this._r.submitLink({...options, sr: this.display_name}) + } + /** + * @summary Submit an image submission to this subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param {object} options An object containing details about the submission. + * @returns The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const blob = await (await fetch("https://example.com/kittens.jpg")).blob() + * subreddit.submitImage({ + * title: 'Take a look at those cute kittens <3', + * imageFile: blob, // Usage as a `Blob`. + * imageFileName: 'kittens.jpg' + * }).then(console.log) + * // => Submission + * // (new image submission created on reddit) + */ + submitImage (options: OmitProps) { + return this._r.submitImage({...options, sr: this.display_name}) + } + /** + * @summary Submit a video or videogif submission to this subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param {object} options An object containing details about the submission. + * @returns The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const mediaVideo = await r.uploadMedia({ + * file: './video.mp4', + * type: 'video' + * }) + * subreddit.submitVideo({ + * title: 'This is a video!', + * videoFile: mediaVideo, // Usage as a `MediaVideo`. + * thumbnailFile: fs.createReadStream('./thumbnail.png'), // Usage as a `stream.Readable`. + * thumbnailFileName: 'thumbnail.png' + * }).then(console.log) + * // => Submission + * // (new video submission created on reddit) + */ + submitVideo (options: OmitProps) { + return this._r.submitVideo({...options, sr: this.display_name}) + } + /** + * @summary Submit a gallery to this subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param {object} options An object containing details about the submission. + * @returns The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const fileinput = document.getElementById('file-input') + * const files = fileinput.files.map(file => { // Usage as an array of `File`s. + * return { + * imageFile: file, + * caption: file.name + * } + * }) + * const blob = await (await fetch("https://example.com/kittens.jpg")).blob() + * const mediaImg = await r.uploadMedia({ // Usage as a `MediaImg`. + * file: blob, + * type: 'img', + * caption: 'cute :3', + * outboundUrl: 'https://example.com/kittens.html' + * }) + * subreddit.submitGallery({ + * title: 'This is a gallery!', + * gallery: [mediaImg, ...files] + * }).then(console.log) + * // => Submission + * // (new gallery submission created on reddit) + */ + submitGallery (options: OmitProps) { + return this._r.submitGallery({...options, sr: this.display_name}) + } + /** + * @summary Creates a new selfpost on this subreddit. + * @param {object} options An object containing details about the submission. + * @returns The newly-created Submission object. + * @example + * + * const mediaVideo = await r.uploadMedia({ + * file: './video.mp4', + * type: 'video', + * caption: 'Short video!' + * }) + * subreddit.submitSelfpost({ + * title: 'This is a selfpost', + * text: 'This is the text body of the selfpost.\n\nAnd This is an inline image {img} And also a video! {vid}', + * inlineMedia: { + * img: { + * file: './animated.gif', // Usage as a file path. + * type: 'img' + * }, + * vid: mediaVideo + * } + * }).then(console.log) + * // => Submission + * // (new selfpost created on reddit) + */ + submitSelfpost (options: OmitProps) { + return this._r.submitSelfpost({...options, sr: this.display_name}) + } + /** + * @summary Submit a poll to this subreddit. (Undocumented endpoint). + * @param {object} options An object containing details about the submission. + * @returns The newly-created Submission object. + * @example + * + * subreddit.submitPoll({ + * title: 'Survey!', + * text: 'Do you like snoowrap?', + * choices: ['YES!', 'NOPE!'], + * duration: 3 + * }).then(console.log) + * // => Submission + * // (new poll submission created on reddit) + */ + submitPoll (options: OmitProps) { + return this._r.submitPoll({...options, sr: this.display_name}) + } + /** + * @summary Creates a new crosspost submission on this subreddit + * @desc **NOTE**: To create a crosspost, the authenticated account must be subscribed to the subreddit where + * the crosspost is being submitted, and that subreddit be configured to allow crossposts. + * @param {object} options An object containing details about the submission + * @returns The newly-created Submission object + * @example + * + * subreddit.submitCrosspost({ + * title: 'I found an interesting post', + * originalPost: '6vths0' + * }).then(console.log) + * // => Submission + * // (new crosspost submission created on reddit) + */ + submitCrosspost (options: OmitProps) { + return this._r.submitCrosspost({...options, sr: this.display_name}) + } + /** + * @summary Gets a Listing of hot posts on this subreddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getSubreddit('snoowrap').getHot().then(console.log) + * // => Listing [ + * // Submission { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getHot (options?: ListingQuery) { + return this._r.getHot(this.display_name, options) + } + /** + * @summary Gets a Listing of new posts on this subreddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getSubreddit('snoowrap').getNew().then(console.log) + * // => Listing [ + * // Submission { ... }, + * // Submission { ... }, + * // ... + * // ] + * + */ + getNew (options?: ListingQuery) { + return this._r.getNew(this.display_name, options) + } + /** + * @summary Gets a Listing of new comments on this subreddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved comments + * @example + * + * r.getSubreddit('snoowrap').getNewComments().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // ... + * // ] + */ + getNewComments (options?: ListingQuery) { + return this._r.getNewComments(this.display_name, options) + } + /** + * @summary Gets a single random Submission from this subreddit. + * @desc **Note**: This function will not work when snoowrap is running in a browser, because the reddit server sends a + * redirect which cannot be followed by a CORS request. + * @returns The retrieved Submission object + * @example + * + * r.getSubreddit('snoowrap').getRandomSubmission().then(console.log) + * // => Submission { ... } + */ + getRandomSubmission () { + return this._r.getRandomSubmission(this.display_name) + } + /** + * @summary Gets a Listing of top posts on this subreddit. + * @param {object} [option] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getSubreddit('snoowrap').getTop({time: 'all'}).then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // ... + * // ] + */ + getTop (options?: ListingQuery) { + return this._r.getTop(this.display_name, options) + } + /** + * @summary Gets a Listing of controversial posts on this subreddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getSubreddit('snoowrap').getControversial({time: 'week'}).then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // ... + * // ] + */ + getControversial (options?: ListingQuery) { + return this._r.getControversial(this.display_name, options) + } + /** + * @summary Gets a Listing of top posts on this subreddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getSubreddit('snoowrap').getRising().then(console.log) + * // => Listing [ + * // Submission { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getRising (options?: ListingQuery) { + return this._r.getRising(this.display_name, options) + } + /** + * @summary Gets the moderator mail for this subreddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing PrivateMessage objects + * @example r.getSubreddit('snoowrap').getModmail().then(console.log) + */ + getModmail (options?: ListingQuery) { + return this._getListing({uri: `r/${this.display_name}/about/message/moderator`, qs: options}) + } + /** + * @summary Gets a list of ModmailConversations from the subreddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getSubreddit('snoowrap').getNewModmailConversations({limit: 2}).then(console.log) + * // => Listing [ + * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... }, + * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... } + * // ] + */ + getNewModmailConversations (options?: ListingQuery) { + return this._r.getNewModmailConversations({...options, entity: this.display_name}) + } + /** + * @summary Gets the moderation log for this subreddit. + * @param {object} [options={}] Options for the resulting Listing + * @returns A Listing containing moderation actions + * @example + * + * r.getSubreddit('snoowrap').getModerationLog().then(console.log) + * + * // => Listing [ + * // ModAction { description: null, mod: 'snoowrap_testing', action: 'editflair', ... } + * // ModAction { description: null, mod: 'snoowrap_testing', action: 'approvecomment', ... } + * // ModAction { description: null, mod: 'snoowrap_testing', action: 'createrule', ... } + * // ] + */ + getModerationLog (options: ModerationLogQuery = {}) { + if (options.mods && !options.mod) options.mod = options.mods.join(',') + delete options.mods + return this._getListing({uri: `r/${this.display_name}/about/log`, qs: options}) + } + /** + * @summary Gets a list of reported items on this subreddit. + * @param {object} [options={}] Options for the resulting Listing + * @returns A Listing containing reported items + * @example + * + * r.getSubreddit('snoowrap').getReports().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getReports (options: FilterableListingQuery = {}) { + return this._getListing({uri: `r/${this.display_name}/about/reports`, qs: options}) + } + /** + * @summary Gets a list of removed items on this subreddit. + * @param {object} [options={}] Options for the resulting Listing + * @returns A Listing containing removed items + * @example + * + * r.getSubreddit('snoowrap').getSpam().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getSpam (options: FilterableListingQuery = {}) { + return this._getListing({uri: `r/${this.display_name}/about/spam`, qs: options}) + } + /** + * @summary Gets a list of items on the modqueue on this subreddit. + * @param {object} [options={}] Options for the resulting Listing + * @returns A Listing containing items on the modqueue + * @example + * + * r.getSubreddit('snoowrap').getModqueue().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getModqueue (options: FilterableListingQuery = {}) { + return this._getListing({uri: `r/${this.display_name}/about/modqueue`, qs: options}) + } + /** + * @summary Gets a list of unmoderated items on this subreddit. + * @param {object} [options={}] Options for the resulting Listing + * @returns A Listing containing unmoderated items + * @example + * + * r.getSubreddit('snoowrap').getUnmoderated().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getUnmoderated (options: FilterableListingQuery = {}) { + return this._getListing({uri: `r/${this.display_name}/about/unmoderated`, qs: options}) + } + /** + * @summary Gets a list of edited items on this subreddit. + * @param {object} [options={}] Options for the resulting Listing + * @returns A Listing containing edited items + * @example + * + * r.getSubreddit('snoowrap').getEdited().then(console.log) + * // => Listing [ + * // Comment { ... }, + * // Comment { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getEdited (options: FilterableListingQuery = {}) { + return this._getListing({uri: `r/${this.display_name}/about/edited`, qs: options}) + } + /** + * @summary Accepts an invite to become a moderator of this subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').acceptModeratorInvite() + */ + async acceptModeratorInvite () { + const res = await this._post({ + url: `r/${this.display_name}/api/accept_moderator_invite`, + form: {api_type} + }) + handleJsonErrors(res) + return this + } + /** + * @summary Abdicates moderator status on this subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').leaveModerator() + */ + async leaveModerator () { + const name = (await this.fetch())!.name + const res = await this._post({url: 'api/leavemoderator', form: {id: name}}) + handleJsonErrors(res) + return this + } + /** + * @summary Abdicates approved submitter status on this subreddit. + * @returns A Promise that resolves with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').leaveContributor() + */ + async leaveContributor () { + const name = (await this.fetch())!.name + const res = await this._post({url: 'api/leavecontributor', form: {id: name}}) + handleJsonErrors(res) + return this + } + /** + * @summary Gets a subreddit's CSS stylesheet. + * @desc **Note**: This function will not work when snoowrap is running in a browser, because the reddit server sends a + * redirect which cannot be followed by a CORS request. + * @desc **Note**: This method will return a 404 error if the subreddit in question does not have a custom stylesheet. + * @returns A Promise for a string containing the subreddit's CSS. + * @example + * + * r.getSubreddit('snoowrap').getStylesheet().then(console.log) + * // => '.md blockquote,.md del,body{color:#121212}.usertext-body ... ' + */ + getStylesheet (): Promise { + return this._get({url: `r/${this.display_name}/stylesheet`}) + } + /** + * @summary Conducts a search of reddit submissions, restricted to this subreddit. + * @param {object} options Search options. Can also contain options for the resulting Listing. + * @example + * + * r.getSubreddit('snoowrap').search({q: 'blah', sort: 'year'}).then(console.log) + * // => Listing [ + * // Submission { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + search (options: OmitProps) { + return this._r.search({...options, subreddit: this, restrict_sr: true}) + } + /** + * @summary Gets the list of banned users on this subreddit. + * @param {object} options Filtering options. Can also contain options for the resulting Listing. + * @returns A Listing of users + * @example + * + * r.getSubreddit('snoowrap').getBannedUsers().then(console.log) + * // => Listing [ + * // { date: 1461720936, note: '', name: 'actually_an_aardvark', id: 't2_q3519' } + * // ... + * // ] + * + */ + getBannedUsers (options?: UserListingQuery) { + return this._getListing({uri: `r/${this.display_name}/about/banned`, qs: options}) + } + /** + * @summary Gets the list of muted users on this subreddit. + * @param {object} options Filtering options. Can also contain options for the resulting Listing. + * @returns A Listing of users + * @example + * + * r.getSubreddit('snoowrap').getBannedUsers().then(console.log) + * // => Listing [ + * // { date: 1461720936, name: 'actually_an_aardvark', id: 't2_q3519' } + * // ... + * // ] + */ + getMutedUsers (options?: UserListingQuery) { + return this._getListing({uri: `r/${this.display_name}/about/muted`, qs: options}) + } + /** + * @summary Gets the list of users banned from this subreddit's wiki. + * @param {object} options Filtering options. Can also contain options for the resulting Listing. + * @returns A Listing of users + * @example + * + * r.getSubreddit('snoowrap').getWikibannedUsers().then(console.log) + * // => Listing [ + * // { date: 1461720936, note: '', name: 'actually_an_aardvark', id: 't2_q3519' } + * // ... + * // ] + */ + getWikibannedUsers (options?: UserListingQuery) { + return this._getListing({uri: `r/${this.display_name}/about/wikibanned`, qs: options}) + } + /** + * @summary Gets the list of approved submitters on this subreddit. + * @param {object} options Filtering options. Can also contain options for the resulting Listing. + * @returns A Listing of users + * @example + * + * r.getSubreddit('snoowrap').getContributors().then(console.log) + * // => Listing [ + * // { date: 1461720936, name: 'actually_an_aardvark', id: 't2_q3519' } + * // ... + * // ] + */ + getContributors (options?: UserListingQuery) { + return this._getListing({uri: `r/${this.display_name}/about/contributors`, qs: options}) + } + /** + * @summary Gets the list of approved wiki submitters on this subreddit . + * @param {object} options Filtering options. Can also contain options for the resulting Listing. + * @returns A Listing of users + * @example + * + * r.getSubreddit('snoowrap').getWikiContributors().then(console.log) + * // => Listing [ + * // { date: 1461720936, name: 'actually_an_aardvark', id: 't2_q3519' } + * // ... + * // ] + */ + getWikiContributors (options?: UserListingQuery) { + return this._getListing({uri: `r/${this.display_name}/about/wikicontributors`, qs: options}) + } + /** + * @summary Gets the list of moderators on this subreddit. + * @param {object} options + * @returns An Array of RedditUsers representing the moderators of this subreddit + * @example + * + * r.getSubreddit('AskReddit').getModerators().then(console.log) + * // => [ + * // RedditUser { date: 1453862639, mod_permissions: [ 'all' ], name: 'not_an_aardvark', id: 't2_k83md' }, + * // ... + * // ] + * + */ + getModerators (options?: UserListingQuery) { + return this._get({url: `r/${this.display_name}/about/moderators`, params: options}) + } + /** + * @summary Deletes the banner for this Subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteBanner() + */ + async deleteBanner () { + const res = await this._post({url: `r/${this.display_name}/api/delete_sr_banner`, form: {api_type}}) + handleJsonErrors(res) + return this + } + /** + * @summary Deletes the header image for this Subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteHeader() + */ + async deleteHeader () { + const res = await this._post({url: `r/${this.display_name}/api/delete_sr_header`, form: {api_type}}) + handleJsonErrors(res) + return this + } + /** + * @summary Deletes this subreddit's icon. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteIcon() + */ + async deleteIcon () { + const res = await this._post({url: `r/${this.display_name}/api/delete_sr_icon`, form: {api_type}}) + handleJsonErrors(res) + return this + } + /** + * @summary Deletes an image from this subreddit. + * @param {string} img_name The name of the image. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').deleteImage() + */ + async deleteImage (img_name: string) { + const res = await this._post({ + url: `r/${this.display_name}/api/delete_sr_img`, + form: {api_type, img_name} + }) + handleJsonErrors(res) + return this + } + /** + * @summary Gets this subreddit's current settings. + * @returns An Object containing this subreddit's current settings. + * @example + * + * r.getSubreddit('snoowrap').getSettings().then(console.log) + * // => SubredditSettings { default_set: true, submit_text: '', subreddit_type: 'private', ... } + */ + getSettings () { + return this._get({url: `r/${this.display_name}/about`}) + } + /** + * @summary Edits this subreddit's settings. + * @param {object} options An Object containing {[option name]: new value} mappings of the options that should be modified. + * Any omitted option names will simply retain their previous values. + * @param {string} options.title The text that should appear in the header of the subreddit + * @param {string} options.public_description The text that appears with this Subreddit on the search page, or on the + * blocked-access page if this subreddit is private. (500 characters max) + * @param {string} options.description The sidebar text for the subreddit. (5120 characters max) + * @param {string} [options.submit_text=''] The text to show below the submission page (1024 characters max) + * @param {boolean} [options.hide_ads=false] Determines whether ads should be hidden on this subreddit. (This is only + * allowed for gold-only subreddits.) + * @param {string} [options.lang='en'] The language of the subreddit (represented as an IETF language tag) + * @param {string} [options.type='public'] Determines who should be able to access the subreddit. This should be one of + * `public, private, restricted, gold_restricted, gold_only, archived, employees_only`. + * @param {string} [options.link_type='any'] Determines what types of submissions are allowed on the subreddit. This should + * be one of `any, link, self`. + * @param {string} [options.submit_link_label=undefined] Custom text to display on the button that submits a link. If + * this is omitted, the default text will be displayed. + * @param {string} [options.submit_text_label=undefined] Custom text to display on the button that submits a selfpost. If + * this is omitted, the default text will be displayed. + * @param {string} [options.wikimode='modonly'] Determines who can edit wiki pages on the subreddit. This should be one of + * `modonly, anyone, disabled`. + * @param {number} [options.wiki_edit_karma=0] The minimum amount of subreddit karma needed for someone to edit this + * subreddit's wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) + * @param {number} [options.wiki_edit_age=0] The minimum account age (in days) needed for someone to edit this subreddit's + * wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) + * @param {string} [options.spam_links='high'] The spam filter strength for links on this subreddit. This should be one of + * `low, high, all`. + * @param {string} [options.spam_selfposts='high'] The spam filter strength for selfposts on this subreddit. This should be + * one of `low, high, all`. + * @param {string} [options.spam_comments='high'] The spam filter strength for comments on this subreddit. This should be one + * of `low, high, all`. + * @param {boolean} [options.over_18=false] Determines whether this subreddit should be classified as NSFW + * @param {boolean} [options.allow_top=true] Determines whether the new subreddit should be able to appear in /r/all and + * trending subreddits + * @param {boolean} [options.show_media=false] Determines whether image thumbnails should be enabled on this subreddit + * @param {boolean} [options.show_media_preview=true] Determines whether media previews should be expanded by default on this + * subreddit + * @param {boolean} [options.allow_images=true] Determines whether image uploads and links to image hosting sites should be + * enabled on this subreddit + * @param {boolean} [options.exclude_banned_modqueue=false] Determines whether posts by site-wide banned users should be + * excluded from the modqueue. + * @param {boolean} [options.public_traffic=false] Determines whether the /about/traffic page for this subreddit should be + * viewable by anyone. + * @param {boolean} [options.collapse_deleted_comments=false] Determines whether deleted and removed comments should be + * collapsed by default + * @param {string} [options.suggested_comment_sort=undefined] The suggested comment sort for the subreddit. This should be + * one of `confidence, top, new, controversial, old, random, qa, live`. If left blank, there will be no suggested sort, + * which means that users will see the sort method that is set in their own preferences (usually `confidence`.) + * @param {boolean} [options.spoilers_enabled=false] Determines whether users can mark their posts as spoilers + * @returns A Promise that fulfills with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').editSettings({submit_text: 'Welcome! Please be sure to read the rules.'}) + */ + async editSettings (options: SubredditOptions) { + const currentValues = await this.getSettings() + const name = (await this.fetch())!.name + await this._r._createOrEditSubreddit({ + ...renameKey(currentValues, 'subreddit_type', 'type'), + ...options, + sr: name + }) + return this + } + /** + * @summary Gets a list of recommended other subreddits given this one. + * @param {object} [options] + * @param {Array} [options.omit=[]] An Array of subreddit names that should be excluded from the listing. + * @returns An Array of subreddit names + * @example + * + * r.getSubreddit('AskReddit').getRecommendedSubreddits().then(console.log) + * // [ 'TheChurchOfRogers', 'Sleepycabin', ... ] + */ + async getRecommendedSubreddits (options: {omit: string[], [key: string]: any}) { + const toOmit = options.omit && options.omit.join(',') + const names = await this._get({url: `api/recommend/sr/${this.display_name}`, params: {omit: toOmit}}) + return map(names, 'sr_name') + } + /** + * @summary Gets the submit text (which displays on the submission form) for this subreddit. + * @returns The submit text, represented as a string. + * @example + * + * r.getSubreddit('snoowrap').getSubmitText().then(console.log) + * // => 'Welcome! Please be sure to read the rules.' + */ + async getSubmitText () { + const res = await this._get({url: `r/${this.display_name}/api/submit_text`}) + return res.submit_text + } + /** + * @summary Updates this subreddit's stylesheet. + * @param {object} options + * @param {string} options.css The new contents of the stylesheet + * @param {string} [options.reason] The reason for the change (256 characters max) + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').updateStylesheet({css: 'body {color:#00ff00;}', reason: 'yay green'}) + */ + async updateStylesheet ({css, reason}) { + const res = await this._post({ + url: `r/${this.display_name}/api/subreddit_stylesheet`, + form: {api_type, op: 'save', reason, stylesheet_contents: css} + }) + handleJsonErrors(res) + return this + } + + async _setSubscribed (status: boolean) { + await this._post({ + url: 'api/subscribe', + form: {action: status ? 'sub' : 'unsub', sr_name: this.display_name} + }) + return this + } + /** + * @summary Subscribes to this subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').subscribe() + */ + subscribe () { + return this._setSubscribed(true) + } + /** + * @summary Unsubscribes from this subreddit. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').unsubscribe() + */ + async unsubscribe () { + /** + * Reddit returns a 404 error if the user attempts to unsubscribe to a subreddit that they weren't subscribed to in the + * first place. It also (as one would expect) returns a 404 error if the subreddit in question does not exist. snoowrap + * should swallow the first type of error internally, but it should raise the second type of error. Unfortunately, the errors + * themselves are indistinguishable. So if a 404 error gets thrown, fetch the current subreddit to check if it exists. If it + * does exist, then the 404 error was of the first type, so swallow it and return the current Subreddit object as usual. If + * the subreddit doesn't exist, then the original error was of the second type, so throw it. + */ + try { + await this._setSubscribed(false) + } catch (e) { + if ((e as AxiosError).response!.status === 404) { + return await this.fetch() + } + throw e + } + return this + } + async _uploadSrImg ({name, file, uploadType, imageType}) { + if (typeof file !== 'string' && !(stream && file instanceof stream.Readable)) { + throw new InvalidMethodCallError('Uploaded image filepath must be a string or a ReadableStream.') + } + const parsedFile = typeof file === 'string' ? fs && fs.createReadStream(file) : file + const result = await this._post({ + url: `r/${this.display_name}/api/upload_sr_img`, + formData: {name, upload_type: uploadType, img_type: imageType, file: parsedFile} + }) + if (result.errors.length) { + throw result.errors[0] + } + return this + } + /** + * @summary Uploads an image for use in this subreddit's stylesheet. + * @param {object} options + * @param {string} options.name The name that the new image should have in the stylesheet + * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an + * image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) in environments (e.g. + * browsers) where the filesystem is unavailable. + * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` + * @returns A Promise that fulfills with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').uploadSubredditImage({name: 'the cookie monster', file: './cookie_monster.png'}) + */ + uploadStylesheetImage ({name, file, image_type = 'png', imageType = image_type}) { + return this._uploadSrImg({name, file, imageType, uploadType: 'img'}) + } + /** + * @summary Uploads an image to use as this subreddit's header. + * @param {object} options + * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an + * image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) for environments (e.g. + * browsers) where the filesystem is unavailable. + * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` + * @returns A Promise that fulfills with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').uploadHeaderImage({name: 'the cookie monster', file: './cookie_monster.png'}) + */ + uploadHeaderImage ({file, image_type = 'png', imageType = image_type}) { + return this._uploadSrImg({file, imageType, uploadType: 'header'}) + } + /** + * @summary Uploads an image to use as this subreddit's mobile icon. + * @param {object} options + * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an + * image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) for environments (e.g. + * browsers) where the filesystem is unavailable. + * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` + * @returns A Promise that fulfills with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').uploadIcon({name: 'the cookie monster', file: './cookie_monster.png'}) + */ + uploadIcon ({file, image_type = 'png', imageType = image_type}) { + return this._uploadSrImg({file, imageType, uploadType: 'icon'}) + } + /** + * @summary Uploads an image to use as this subreddit's mobile banner. + * @param {object} options + * @param {string|stream.Readable} options.file The image file that should get uploaded. This should either be the path to an + * image file, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) for environments (e.g. + * browsers) where the filesystem is unavailable. + * @param {string} [options.imageType='png'] Determines how the uploaded image should be stored. One of `png, jpg` + * @returns A Promise that fulfills with this Subreddit when the request is complete. + * @example r.getSubreddit('snoowrap').uploadBannerImage({name: 'the cookie monster', file: './cookie_monster.png'}) + */ + uploadBannerImage ({file, image_type = 'png', imageType = image_type}) { + return this._uploadSrImg({file, imageType, upload_type: 'banner'}) + } + /** + * @summary Gets information on this subreddit's rules. + * @returns A Promise that fulfills with information on this subreddit's rules. + * @example + * + * r.getSubreddit('snoowrap').getRules().then(console.log) + * + * // => { + * rules: [ + * { + * kind: 'all', + * short_name: 'Rule 1: No violating rule 1', + * description: 'Breaking this rule is not allowed.', + * ... + * }, + * ... + * ], + * site_rules: [ + * 'Spam', + * 'Personal and confidential information'', + * 'Threatening, harassing, or inciting violence' + * ] + * } + */ + getRules () { + return this._get({url: `r/${this.display_name}/about/rules`}) + } + /** + * @summary Gets the stickied post on this subreddit, or throws a 404 error if none exists. + * @param {object} [options] + * @param {number} [options.num=1] The number of the sticky to get. Should be either `1` (first sticky) or `2` (second sticky). + * @returns A Submission object representing this subreddit's stickied submission + * @example + * r.getSubreddit('snoowrap').getSticky({num: 2}) + * // => Submission { ... } + */ + getSticky ({num = 1} = {}) { + return this._get({url: `r/${this.display_name}/about/sticky`, params: {num}}) + } + async _friend (options) { + const res = await this._post({ + url: `r/${this.display_name}/api/friend`, + form: {...options, api_type} + }) + handleJsonErrors(res) + return this + } + async _unfriend (options) { + const res = await this._post({ + url: `r/${this.display_name}/api/unfriend`, + form: {...options, api_type} + }) + handleJsonErrors(res) + return this + } + /** + * @summary Invites the given user to be a moderator of this subreddit. + * @param {object} options + * @param {string} options.name The username of the account that should be invited + * @param {Array} [options.permissions] The moderator permissions that this user should have. This should be an array + * containing some combination of `"wiki", "posts", "access", "mail", "config", "flair"`. To add a moderator with full + * permissions, omit this property entirely. + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').inviteModerator({name: 'actually_an_aardvark', permissions: ['posts', 'wiki']}) + */ + inviteModerator ({name, permissions}) { + return this._friend({name, permissions: formatModPermissions(permissions), type: 'moderator_invite'}) + } + /** + * @summary Revokes an invitation for the given user to be a moderator. + * @param {object} options + * @param {string} options.name The username of the account whose invitation should be revoked + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').revokeModeratorInvite({name: 'actually_an_aardvark'}) + */ + revokeModeratorInvite ({name}) { + return this._unfriend({name, type: 'moderator_invite'}) + } + /** + * @summary Removes the given user's moderator status on this subreddit. + * @param {object} options + * @param {string} options.name The username of the account whose moderator status should be removed + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').removeModerator({name: 'actually_an_aardvark'}) + */ + removeModerator ({name}) { + return this._unfriend({name, type: 'moderator'}) + } + /** + * @summary Makes the given user an approved submitter of this subreddit. + * @param {object} options + * @param {string} options.name The username of the account that should be given this status + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').addContributor({name: 'actually_an_aardvark'}) + */ + addContributor ({name}) { + return this._friend({name, type: 'contributor'}) + } + /** + * @summary Revokes this user's approved submitter status on this subreddit. + * @param {object} options + * @param {string} options.name The username of the account whose status should be revoked + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').removeContributor({name: 'actually_an_aardvark'}) + */ + removeContributor ({name}) { + return this._unfriend({name, type: 'contributor'}) + } + /** + * @summary Bans the given user from this subreddit. + * @param {object} options + * @param {string} options.name The username of the account that should be banned + * @param {string} [options.banMessage] The ban message. This will get sent to the user in a private message, alerting them + * that they have been banned. + * @param {string} [options.banReason] A string indicating which rule the banned user broke (100 characters max) + * @param {number} [options.duration] The duration of the ban, in days. For a permanent ban, omit this parameter. + * @param {string} [options.banNote] A note that appears on the moderation log, usually used to indicate the reason for the + * ban. This is not visible to the banned user. (300 characters max) + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').banUser({name: 'actually_an_aardvark', banMessage: 'You are now banned LOL'}) + */ + banUser ({ + name, + ban_message, banMessage = ban_message, + ban_reason, banReason = ban_reason, + duration, + ban_note, banNote = ban_note + }) { + return this._friend({ + name, ban_message: banMessage, + ban_reason: banReason, + duration, + note: banNote, + type: 'banned' + }) + } + /** + * @summary Unbans the given user from this subreddit. + * @param {object} options + * @param {string} options.name The username of the account that should be unbanned + * @returns A Promise that fulfills when the request is complete + * @example r.getSubreddit('snoowrap').unbanUser({name: 'actually_an_aardvark'}) + */ + unbanUser ({name}) { + return this._unfriend({name, type: 'banned'}) + } + /** + * @summary Mutes the given user from messaging this subreddit for 72 hours. + * @param {object} options + * @param {string} options.name The username of the account that should be muted + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').muteUser({name: 'actually_an_aardvark'}) + */ + muteUser ({name}) { + return this._friend({name, type: 'muted'}) + } + /** + * @summary Unmutes the given user from messaging this subreddit. + * @param {object} options + * @param {string} options.name The username of the account that should be muted + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').unmuteUser({name: 'actually_an_aardvark'}) + */ + unmuteUser ({name}) { + return this._unfriend({name, type: 'muted'}) + } + /** + * @summary Bans the given user from editing this subreddit's wiki. + * @param {object} options + * @param {string} options.name The username of the account that should be wikibanned + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').wikibanUser({name: 'actually_an_aardvark'}) + */ + wikibanUser ({name}) { + return this._friend({name, type: 'wikibanned'}) + } + /** + * @summary Unbans the given user from editing this subreddit's wiki. + * @param {object} options + * @param {string} options.name The username of the account that should be unwikibanned + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').unwikibanUser({name: 'actually_an_aardvark'}) + */ + unwikibanUser ({name}) { + return this._unfriend({name, type: 'wikibanned'}) + } + /** + * @summary Adds the given user to this subreddit's list of approved wiki editors. + * @param {object} options + * @param {string} options.name The username of the account that should be given approved editor status + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').addWikiContributor({name: 'actually_an_aardvark'}) + */ + addWikiContributor ({name}) { + return this._friend({name, type: 'wikicontributor'}) + } + /** + * @summary Removes the given user from this subreddit's list of approved wiki editors. + * @param {object} options + * @param {string} options.name The username of the account whose approved editor status should be revoked + * @returns A Promise that fulfills with this Subreddit when the request is complete + * @example r.getSubreddit('snoowrap').removeWikiContributor({name: 'actually_an_aardvark'}) + */ + removeWikiContributor ({name}) { + return this._unfriend({name, type: 'wikicontributor'}) + } + /** + * @summary Sets the permissions for a given moderator on this subreddit. + * @param {object} options + * @param {string} options.name The username of the moderator whose permissions are being changed + * @param {Array} [options.permissions] The new moderator permissions that this user should have. This should be an array + * containing some combination of `"wiki", "posts", "access", "mail", "config", "flair"`. To add a moderator with full + * permissions, omit this property entirely. + * @returns A Promise that fulfills with this Subreddit when this request is complete + * @example r.getSubreddit('snoowrap').setModeratorPermissions({name: 'actually_an_aardvark', permissions: ['mail']}) + */ + async setModeratorPermissions ({name, permissions}) { + const res = await this._post({ + url: `r/${this.display_name}/api/setpermissions`, + form: {api_type, name, permissions: formatModPermissions(permissions), type: 'moderator'} + }) + handleJsonErrors(res) + return this + } + /** + * @summary Gets a given wiki page on this subreddit. + * @param {string} title The title of the desired wiki page. + * @returns {WikiPage} An unfetched WikiPage object corresponding to the desired wiki page + * @example + * + * r.getSubreddit('snoowrap').getWikiPage('index') + * // => WikiPage { title: 'index', subreddit: Subreddit { display_name: 'snoowrap' } } + */ + getWikiPage (title: string) { + return this._r._newObject('WikiPage', {subreddit: this, title}) + } + /** + * @summary Gets the list of wiki pages on this subreddit. + * @returns An Array containing WikiPage objects + * @example + * + * r.getSubreddit('snoowrap').getWikiPages().then(console.log) + * // => [ + * // WikiPage { title: 'index', subreddit: Subreddit { display_name: 'snoowrap'} } + * // WikiPage { title: 'config/sidebar', subreddit: Subreddit { display_name: 'snoowrap'} } + * // WikiPage { title: 'secret_things', subreddit: Subreddit { display_name: 'snoowrap'} } + * // WikiPage { title: 'config/submit_text', subreddit: Subreddit { display_name: 'snoowrap'} } + * // ] + */ + async getWikiPages () { + const res: string[] = await this._get({url: `r/${this.display_name}/wiki/pages`}) + return res.map(title => this.getWikiPage(title)) + } + /** + * @summary Gets a list of revisions on this subreddit's wiki. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing wiki revisions + * @example + * + * r.getSubreddit('snoowrap').getWikiRevisions().then(console.log) + * // => Listing [ + * // { page: 'index', reason: 'added cookies', ... }, + * // ... + * // ] + */ + getWikiRevisions (options: ListingQuery) { + return this._getListing({uri: `r/${this.display_name}/wiki/revisions`, qs: options}) + } +} + +export default Subreddit diff --git a/src/objects/UserList.js b/src/objects/UserList.js deleted file mode 100644 index 46e46ab1..00000000 --- a/src/objects/UserList.js +++ /dev/null @@ -1,5 +0,0 @@ -export default class UserList { - constructor (options, _r) { - return options.children.map(user => _r._newObject('RedditUser', user)); - } -} diff --git a/src/objects/UserList.ts b/src/objects/UserList.ts new file mode 100644 index 00000000..330084df --- /dev/null +++ b/src/objects/UserList.ts @@ -0,0 +1,16 @@ +import type snoowrap from '../snoowrap' + +export interface Options { + children: {[key: string]: any}[] + [key: string]: any +} + +export default class UserList { + ['construsctor']: typeof UserList + static _name = 'UserList' + + constructor (options: Options, _r: snoowrap) { + // @ts-ignore + return options.children.map(user => _r._newObject('RedditUser', user)) + } +} diff --git a/src/objects/VoteableContent.d.ts b/src/objects/VoteableContent.d.ts deleted file mode 100644 index 6fdc4c86..00000000 --- a/src/objects/VoteableContent.d.ts +++ /dev/null @@ -1,94 +0,0 @@ -import RedditUser from './RedditUser'; -import ReplyableContent from './ReplyableContent'; -import Subreddit from './Subreddit'; - -export interface RichTextFlair { - /** The string representation of the emoji */ - a?: string; - /** The type of the flair entry */ - e: 'text' | 'emoji'; - /** URL of the emoji image */ - u?: string; - /** The text content of a text flair */ - t?: string; -} - -interface Gildings { - /** Number of Reddit Silver awarded */ - gid_1: number; - /** Number of Reddit Gold awarded */ - gid_2: number; - /** Number of Reddit Platinum awarded */ - gid_3: number; -} - -export type SubredditType = - | 'gold_restricted' - | 'archived' - | 'restricted' - | 'employees_only' - | 'gold_only' - | 'private' - | 'user' - | 'public'; - -export default class VoteableContent extends ReplyableContent { - approved_at_utc: number | null; - approved_by: RedditUser | null; - archived: boolean; - author: RedditUser; - author_flair_background_color: string | null; - author_flair_css_class: string | null; - author_flair_richtext: RichTextFlair[]; - author_flair_template_id: string | null; - author_flair_text: string | null; - author_flair_text_color: string | null; - author_flair_type: 'text' | 'richtext'; - author_fullname: string; - author_patreon_flair: boolean; - banned_at_utc: number | null; - banned_by: RedditUser | null; - can_gild: boolean; - can_mod_post: boolean; - distinguished: 'admin' | 'moderator' | null; - downs: number; - edited: number | boolean; - gilded: number; - gildings: Gildings; - /** true = upvoted, false = downvoted, null = hasn't voted */ - likes: boolean | null; - mod_note: string; - /** The name of the user that added the mod_note */ - mod_reason_by: string; - mod_reason_title: string; - mod_reports: string[]; - no_follow: boolean; - num_reports: number; - permalink: string; - removal_reason: any; // ? - report_reasons: string[]; - saved: boolean; - score: number; - send_replies: boolean; - stickied: boolean; - subreddit: Subreddit; - subreddit_id: string; - subreddit_name_prefixed: string; - subreddit_type: SubredditType; - ups: number; - user_reports: string[]; - - delete(): Promise; - disableInboxReplies(): Promise; - distinguish(options?: { status?: boolean | string; sticky?: boolean; }): Promise; - downvote(): Promise; - edit(updatedText: string): Promise; - enableInboxReplies(): Promise; - expandReplies(options?: { limit?: number; depth?: number; }): Promise; - gild(): Promise; - save(): Promise; - undistinguish(): Promise; - unsave(): Promise; - unvote(): Promise; - upvote(): Promise; -} diff --git a/src/objects/VoteableContent.js b/src/objects/VoteableContent.js deleted file mode 100644 index b83fb904..00000000 --- a/src/objects/VoteableContent.js +++ /dev/null @@ -1,205 +0,0 @@ -import Promise from '../Promise.js'; -import {handleJsonErrors} from '../helpers.js'; -import ReplyableContent from './ReplyableContent.js'; - -const api_type = 'json'; - -/** -* A set of mixin functions that apply to Submissions and Comments. -* -* @extends ReplyableContent -*/ -const VoteableContent = class VoteableContent extends ReplyableContent { - /** - * @summary Casts a vote on this Comment or Submission. - * @private - * @param {number} direction The direction of the vote. (1 for an upvote, -1 for a downvote, 0 to remove a vote) - * @returns {Promise} A Promise that fulfills when the request is complete. - */ - _vote (direction) { - return this._post({uri: 'api/vote', form: {dir: direction, id: this.name}}).return(this); - } - /** - * @summary Upvotes this Comment or Submission. - * @returns {Promise} A Promise that fulfills with this Comment/Submission when the request is complete - * @desc **Note: votes must be cast by humans.** That is, API clients proxying a human's action one-for-one are OK, - but bots deciding how to vote on content or amplifying a human's vote are not. See the - [reddit rules](https://reddit.com/rules) for more details on what constitutes vote cheating. (This guideline is quoted from - [the official reddit API documentation page](https://www.reddit.com/dev/api#POST_api_vote).) - * @example r.getSubmission('4e62ml').upvote() - */ - upvote () { - return this._vote(1); - } - /** - * @summary Downvotes this Comment or Submission. - * @returns {Promise} A Promise that fulfills with this Comment/Submission when the request is complete. - * @desc **Note: votes must be cast by humans.** That is, API clients proxying a human's action one-for-one are OK, but - bots deciding how to vote on content or amplifying a human's vote are not. See the [reddit rules](https://reddit.com/rules) - for more details on what constitutes vote cheating. (This guideline is quoted from - [the official reddit API documentation page](https://www.reddit.com/dev/api#POST_api_vote).) - * @example r.getSubmission('4e62ml').downvote() - */ - downvote () { - return this._vote(-1); - } - /** - * @summary Removes any existing vote on this Comment or Submission. - * @returns {Promise} A Promise that fulfills with this Comment/Submission when the request is complete. - * @desc **Note: votes must be cast by humans.** That is, API clients proxying a human's action one-for-one are OK, but - bots deciding how to vote on content or amplifying a human's vote are not. See the [reddit rules](https://reddit.com/rules) - for more details on what constitutes vote cheating. (This guideline is quoted from - [the official reddit API documentation page](https://www.reddit.com/dev/api#POST_api_vote).) - * @example r.getSubmission('4e62ml').unvote() - */ - unvote () { - return this._vote(0); - } - /** - * @summary Saves this Comment or Submission (i.e. adds it to the list at reddit.com/saved) - * @returns {Promise} A Promise that fulfills when the request is complete - * @example r.getSubmission('4e62ml').save() - */ - save () { - return this._post({uri: 'api/save', form: {id: this.name}}).return(this); - } - /** - * @summary Unsaves this item - * @returns {Promise} A Promise that fulfills when the request is complete - * @example r.getSubmission('4e62ml').unsave() - */ - unsave () { - return this._post({uri: 'api/unsave', form: {id: this.name}}).return(this); - } - /** - * @summary Distinguishes this Comment or Submission with a sigil. - * @desc **Note:** This function will only work if the requester is the author of this Comment/Submission. - * @param {object} options - * @param {boolean|string} [options.status=true] Determines how the item should be distinguished. - `true` (default) signifies that the item should be moderator-distinguished, and - `false` signifies that the item should not be distinguished. Passing a string (e.g. - `admin`) will cause the item to get distinguished with that string, if possible. - * @param {boolean} [options.sticky=false] Determines whether this item should be stickied in addition to being - distinguished. (This only applies to comments; to sticky a submission, use {@link Submission#sticky} instead.) - * @returns {Promise} A Promise that fulfills when the request is complete. - * @example r.getComment('d1xclfo').distinguish({status: true, sticky: true}) - */ - distinguish ({status = true, sticky = false} = {}) { - return this._post({uri: 'api/distinguish', form: { - api_type, - how: status === true ? 'yes' : status === false ? 'no' : status, - sticky, - id: this.name - }}).return(this); - } - /** - * @summary Undistinguishes this Comment or Submission. Alias for distinguish({status: false}) - * @returns {Promise} A Promise that fulfills when the request is complete. - * @example r.getSubmission('4e62ml').undistinguish() - */ - undistinguish () { - return this.distinguish({status: false, sticky: false}).return(this); - } - /** - * @summary Edits this Comment or Submission. - * @param {string} updatedText The updated markdown text to use - * @returns {Promise} A Promise that fulfills when this request is complete. - * @example r.getComment('coip909').edit('Blah blah blah this is new updated text') - */ - edit (updatedText) { - return this._post({ - uri: 'api/editusertext', - form: {api_type, text: updatedText, thing_id: this.name} - }).tap(handleJsonErrors(this)); - } - /** - * @summary Gives reddit gold to the author of this Comment or Submission. - * @returns {Promise} A Promise that fullfills with this Comment/Submission when this request is complete - * @example r.getComment('coip909').gild() - */ - gild () { - return this._post({uri: `api/v1/gold/gild/${this.name}`}).return(this); - } - _setInboxRepliesEnabled (state) { - return this._post({uri: 'api/sendreplies', form: {state, id: this.name}}); - } - /** - * @summary Enables inbox replies on this Comment or Submission - * @returns {Promise} A Promise that fulfills with this content when the request is complete - * @example r.getComment('coip909').enableInboxReplies() - */ - enableInboxReplies () { - return this._setInboxRepliesEnabled(true).return(this); - } - /** - * @summary Disables inbox replies on this Comment or Submission - * @returns {Promise} A Promise that fulfills with this content when the request is complete - * @example r.getComment('coip909').disableInboxReplies() - */ - disableInboxReplies () { - return this._setInboxRepliesEnabled(false).return(this); - } - _mutateAndExpandReplies ({limit, depth}) { - if (depth <= 0) { - return Promise.resolve(this); - } - const repliesKey = this.constructor._name === 'Submission' ? 'comments' : 'replies'; - return this[repliesKey].fetchMore({amount: limit - this[repliesKey].length}) - .tap(replies => { - this[repliesKey] = replies; - }) - .then(replies => replies.slice(0, limit)) - .map(reply => reply._mutateAndExpandReplies({limit, depth: depth - 1})) - .return(this); - } - /** - * @summary Expands the reply Listings on this Comment/Submission. - * @desc This is useful in cases where one wants to enumerate all comments on a - thread, even the ones that are initially hidden when viewing it (e.g. long comment chains). - * - * This function accepts two optional parameters `options.limit` and `options.depth`. `options.limit` sets an upper bound - for the branching factor of the resulting replies tree, i.e. the number of comments that are fetched in reply to any given - item. `options.depth` sets an upper bound for the depth of the resulting replies tree (where a depth of 0 signifies that no - replies should be fetched at all). - * - * Note that regardless of the `limit` and `depth` parameters used, any reply that appeared in the original reply tree will - appear in the expanded reply tree. In certain cases, the depth of the resulting tree may also be larger than `options.depth`, - if the reddit API returns more of a comment tree than needed. - * - * These parameters should primarily be used to keep the request count low; if a precise limit and depth are needed, it is - recommended to manually verify the comments in the tree afterwards. - * - * Both parameters default to `Infinity` if omitted, i.e. the resulting tree contains every single comment available. It should - be noted that depending on the size and depth of the thread, fetching every single comment can use up a significant number - of ratelimited requests. (To give an intuitive estimate, consider how many clicks would be needed to view all the - comments on the thread using the HTML site.) - * @param {object} [options={}] - * @param {number} [options.limit=Infinity] An upper-bound for the branching factor of the resulting tree of replies - * @param {number} [options.depth=Infinity] An upper-bound for the depth of the resulting tree of replies - * @returns {Promise} A Promise that fulfills with a new version of this object that has an expanded reply tree. The original - object is not modified - * @example r.getSubmission('4fuq26').expandReplies().then(console.log) - * // => (a very large comment tree containing every viewable comment on this thread) - */ - expandReplies ({limit = Infinity, depth = Infinity} = {}) { - return this._r._promiseWrap(this.fetch().then(result => { - return result._clone({deep: true})._mutateAndExpandReplies({limit, depth}); - })); - } -}; - -// VoteableContent#delete is not in the class body since Safari 9 can't parse the `delete` function name in class bodies. -/** -* @function -* @name delete -* @summary Deletes this Comment or Submission -* @returns {Promise} A Promise that fulfills with this Comment/Submission when this request is complete -* @example r.getComment('coip909').delete() -* @memberof VoteableContent -* @instance -*/ -Object.defineProperty(VoteableContent.prototype, 'delete', {value () { - return this._post({uri: 'api/del', form: {id: this.name}}).return(this); -}, configurable: true, writable: true}); - -export default VoteableContent; diff --git a/src/objects/VoteableContent.ts b/src/objects/VoteableContent.ts new file mode 100644 index 00000000..d941fa03 --- /dev/null +++ b/src/objects/VoteableContent.ts @@ -0,0 +1,279 @@ +import {handleJsonErrors} from '../helper' +import ReplyableContent from './ReplyableContent' +import type {RedditUser, Subreddit} from './' +import type {RichTextFlair, Gildings, SubredditType} from '../interfaces' + +const api_type = 'json' + + +interface VoteableContent { + approved?: boolean + approved_at_utc: number|null + approved_by: RedditUser|null + archived: boolean + author: RedditUser + author_flair_background_color: string|null + author_flair_css_class: string|null + author_flair_richtext: RichTextFlair[] + author_flair_template_id: string|null + author_flair_text: string|null + author_flair_text_color: string|null + author_flair_type: 'text'|'richtext' + author_fullname: string + author_patreon_flair: boolean + banned_at_utc: number|null + banned_by: RedditUser|null + can_gild: boolean + can_mod_post: boolean + created: number + created_utc: number + distinguished: 'admin'|'moderator'|null + downs: number + edited: number|boolean + gilded: number + gildings: Gildings + id: string + locked: boolean + /** true = upvoted, false = downvoted, null = hasn't voted */ + likes: boolean|null + mod_note: string + /** The name of the user that added the mod_note */ + mod_reason_by: string + mod_reason_title: string + mod_reports: string[] + name: string + no_follow: boolean + num_reports: number + permalink: string + removal_reason: any // ? + report_reasons: string[] + saved: boolean + score: number + send_replies: boolean + stickied: boolean + subreddit: Subreddit + subreddit_id: string + subreddit_name_prefixed: string + subreddit_type: SubredditType + ups: number + user_reports: string[] + delete(): Promise +} + +/** + * A set of mixin functions that apply to Submissions and Comments. + */ +class VoteableContent> extends ReplyableContent { + static _name = 'VoteableContent' + + async _mutateAndExpandReplies (limit: number, depth: number) { + if (depth <= 0) { + return this + } + const repliesKey = this.constructor._name === 'Submission' ? 'comments' : 'replies' + const replies = await this[repliesKey].fetchMore({amount: limit - this[repliesKey].length}) + this[repliesKey] = replies + replies.slice(0, limit).map((reply: any) => reply._mutateAndExpandReplies(limit, depth - 1)) + return this + } + + _setInboxRepliesEnabled (state: boolean) { + return this._post({url: 'api/sendreplies', form: {state, id: this.name}}) + } + + /** + * @summary Casts a vote on this Comment or Submission. + * @private + * @param direction The direction of the vote. (1 for an upvote, -1 for a downvote, 0 to remove a vote) + * @returns A Promise that fulfills when the request is complete. + */ + async _vote (direction: number) { + await this._post({url: 'api/vote', form: {dir: direction, id: this.name}}) + return this + } + + /** + * @summary Disables inbox replies on this Comment or Submission + * @returns A Promise that fulfills with this content when the request is complete + * @example r.getComment('coip909').disableInboxReplies() + */ + async disableInboxReplies () { + await this._setInboxRepliesEnabled(false) + return this + } + + /** + * @summary Distinguishes this Comment or Submission with a sigil. + * @desc **Note:** This function will only work if the requester is the author of this Comment/Submission. + * @param {boolean|string} [status=true] Determines how the item should be distinguished. + * `true` (default) signifies that the item should be moderator-distinguished, and + * `false` signifies that the item should not be distinguished. Passing a string (e.g. + * `admin`) will cause the item to get distinguished with that string, if possible. + * @param {boolean} [sticky=false] Determines whether this item should be stickied in addition to being + * distinguished. (This only applies to comments; to sticky a submission, use {@link Submission#sticky} instead.) + * @returns A Promise that fulfills when the request is complete. + * @example r.getComment('d1xclfo').distinguish({status: true, sticky: true}) + */ + async distinguish (status: boolean|string = true, sticky = false) { + await this._post({url: 'api/distinguish', form: { + api_type, + how: status === true ? 'yes' : status === false ? 'no' : status, + sticky, + id: this.name + }}) + return this + } + + /** + * @summary Downvotes this Comment or Submission. + * @returns A Promise that fulfills with this Comment/Submission when the request is complete. + * @desc **Note: votes must be cast by humans.** That is, API clients proxying a human's action one-for-one are OK, but + * bots deciding how to vote on content or amplifying a human's vote are not. See the [reddit rules](https://reddit.com/rules) + * for more details on what constitutes vote cheating. (This guideline is quoted from + * [the official reddit API documentation page](https://www.reddit.com/dev/api#POST_api_vote).) + * @example r.getSubmission('4e62ml').downvote() + */ + downvote () { + return this._vote(-1) + } + + /** + * @summary Edits this Comment or Submission. + * @param {string} updatedText The updated markdown text to use + * @returns A Promise that fulfills when this request is complete. + * @example r.getComment('coip909').edit('Blah blah blah this is new updated text') + */ + async edit (updatedText: string) { + const res = await this._post({ + url: 'api/editusertext', + form: {api_type, text: updatedText, thing_id: this.name} + }) + handleJsonErrors(res) + return this + } + + /** + * @summary Enables inbox replies on this Comment or Submission + * @returns A Promise that fulfills with this content when the request is complete + * @example r.getComment('coip909').enableInboxReplies() + */ + async enableInboxReplies () { + await this._setInboxRepliesEnabled(true) + return this + } + + /** + * @summary Expands the reply Listings on this Comment/Submission. + * @desc This is useful in cases where one wants to enumerate all comments on a + * thread, even the ones that are initially hidden when viewing it (e.g. long comment chains). + * + * This function accepts two optional parameters `options.limit` and `options.depth`. `options.limit` sets an upper bound + * for the branching factor of the resulting replies tree, i.e. the number of comments that are fetched in reply to any given + * item. `options.depth` sets an upper bound for the depth of the resulting replies tree (where a depth of 0 signifies that no + * replies should be fetched at all). + * + * Note that regardless of the `limit` and `depth` parameters used, any reply that appeared in the original reply tree will + * appear in the expanded reply tree. In certain cases, the depth of the resulting tree may also be larger than `options.depth`, + * if the reddit API returns more of a comment tree than needed. + * + * These parameters should primarily be used to keep the request count low; if a precise limit and depth are needed, it is + * recommended to manually verify the comments in the tree afterwards. + * + * Both parameters default to `Infinity` if omitted, i.e. the resulting tree contains every single comment available. It should + * be noted that depending on the size and depth of the thread, fetching every single comment can use up a significant number + * of ratelimited requests. (To give an intuitive estimate, consider how many clicks would be needed to view all the + * comments on the thread using the HTML site.) + * @param {number} [limit=Infinity] An upper-bound for the branching factor of the resulting tree of replies + * @param {number} [depth=Infinity] An upper-bound for the depth of the resulting tree of replies + * @returns A Promise that fulfills with a new version of this object that has an expanded reply tree. The original + * object is not modified + * @example r.getSubmission('4fuq26').expandReplies().then(console.log) + * // => (a very large comment tree containing every viewable comment on this thread) + */ + async expandReplies (limit = Infinity, depth = Infinity) { + await this.fetch() + return this._clone(true)._mutateAndExpandReplies(limit, depth) + } + + /** + * @summary Gives reddit gold to the author of this Comment or Submission. + * @returns A Promise that fullfills with this Comment/Submission when this request is complete + * @example r.getComment('coip909').gild() + */ + async gild () { + await this._post({url: `api/v1/gold/gild/${this.name}`}) + return this + } + + /** + * @summary Saves this Comment or Submission (i.e. adds it to the list at reddit.com/saved) + * @returns A Promise that fulfills when the request is complete + * @example r.getSubmission('4e62ml').save() + */ + async save () { + await this._post({url: 'api/save', form: {id: this.name}}) + return this + } + + /** + * @summary Undistinguishes this Comment or Submission. Alias for distinguish({status: false}) + * @returns A Promise that fulfills when the request is complete. + * @example r.getSubmission('4e62ml').undistinguish() + */ + undistinguish () { + return this.distinguish(false, false) + } + + /** + * @summary Unsaves this item + * @returns A Promise that fulfills when the request is complete + * @example r.getSubmission('4e62ml').unsave() + */ + async unsave () { + await this._post({url: 'api/unsave', form: {id: this.name}}) + return this + } + + /** + * @summary Removes any existing vote on this Comment or Submission. + * @returns A Promise that fulfills with this Comment/Submission when the request is complete. + * @desc **Note: votes must be cast by humans.** That is, API clients proxying a human's action one-for-one are OK, but + * bots deciding how to vote on content or amplifying a human's vote are not. See the [reddit rules](https://reddit.com/rules) + * for more details on what constitutes vote cheating. (This guideline is quoted from + * [the official reddit API documentation page](https://www.reddit.com/dev/api#POST_api_vote).) + * @example r.getSubmission('4e62ml').unvote() + */ + unvote () { + return this._vote(0) + } + + /** + * @summary Upvotes this Comment or Submission. + * @returns A Promise that fulfills with this Comment/Submission when the request is complete + * @desc **Note: votes must be cast by humans.** That is, API clients proxying a human's action one-for-one are OK, + * but bots deciding how to vote on content or amplifying a human's vote are not. See the + * [reddit rules](https://reddit.com/rules) for more details on what constitutes vote cheating. (This guideline is quoted from + * [the official reddit API documentation page](https://www.reddit.com/dev/api#POST_api_vote).) + * @example r.getSubmission('4e62ml').upvote() + */ + upvote () { + return this._vote(1) + } +} + +// VoteableContent#delete is not in the class body since Safari 9 can't parse the `delete` function name in class bodies. +/** + * @function + * @name delete + * @summary Deletes this Comment or Submission + * @returns A Promise that fulfills with this Comment/Submission when this request is complete + * @example r.getComment('coip909').delete() + * @memberof VoteableContent + * @instance + */ +Object.defineProperty(VoteableContent.prototype, 'delete', {async value () { + await this._post({url: 'api/del', form: {id: this.name}}) + return this +}, configurable: true, writable: true}) + +export default VoteableContent diff --git a/src/objects/WikiPage.d.ts b/src/objects/WikiPage.d.ts deleted file mode 100644 index 88f16e2f..00000000 --- a/src/objects/WikiPage.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Listing, { ListingOptions } from './Listing'; -import RedditContent from './RedditContent'; -import RedditUser from './RedditUser'; -import Submission from './Submission'; - -export default class WikiPage extends RedditContent { - content_html: string; - content_md: string; - may_revise: boolean; - revision_by: RedditUser; - revision_date: number; - - addEditor(options: { name: string; }): Promise; - edit(options: EditOptions): Promise; - editSettings(options: Settings): Promise; - getDiscussions(options?: ListingOptions): Promise>; - getRevisions(options?: ListingOptions): Promise>; - getSettings(): Promise; - hideRevision(options: { id: string; }): Promise; - removeEditor(options: { name: string; }): Promise; - revert(options: { id: string; }): Promise; -} - -interface Settings { - listed: boolean; - permissionLevel: 0 | 1 | 2; -} - -interface EditOptions { - text: string; - reason?: string; - perviousRevision?: string; -} - -export interface WikiPageRevision { - timestamp: number; - reason: string; - page: string; - id: string; - author: RedditUser; -} diff --git a/src/objects/WikiPage.js b/src/objects/WikiPage.js deleted file mode 100644 index 233e1352..00000000 --- a/src/objects/WikiPage.js +++ /dev/null @@ -1,154 +0,0 @@ -import RedditContent from './RedditContent.js'; - -/** -* A class representing a wiki page on a subreddit. -* -* **Note:** Due to a bug in reddit's CORS settings, it is not possible to fetch the contents of a wiki page on a private -subreddit while running snoowrap in a browser. (This issue does not apply when running snoowrap in Node.js.) -* -* -* @extends RedditContent -* @example -* -* // Get a wiki page on a given subreddit by name -* r.getSubreddit('AskReddit').getWikiPage('rules') -*/ -const WikiPage = class WikiPage extends RedditContent { - get _uri () { - return `r/${this.subreddit.display_name}/wiki/${this.title}`; - } - /** - * @summary Gets the current settings for this wiki page. - * @returns {Promise} An Object representing the settings for this page - * @example - * - * r.getSubreddit('snoowrap').getWikiPage('index').getSettings().then(console.log) - * // => WikiPageSettings { permlevel: 0, editors: [], listed: true } - */ - getSettings () { - return this._get({uri: `r/${this.subreddit.display_name}/wiki/settings/${this.title}`}); - } - /** - * @summary Edits the settings for this wiki page. - * @param {object} options - * @param {boolean} options.listed Determines whether this wiki page should appear on the public list of pages for this - subreddit. - * @param {number} options.permissionLevel Determines who should be allowed to access and edit this page `0` indicates that - this subreddit's default wiki settings should get used, `1` indicates that only approved wiki contributors on this subreddit - should be able to edit this page, and `2` indicates that only mods should be able to view and edit this page. - * @returns {Promise} A Promise that fulfills with this WikiPage when the request is complete - * @example r.getSubreddit('snoowrap').getWikiPage('index').editSettings({listed: false, permission_level: 1}) - */ - editSettings ({listed, permission_level, permissionLevel = permission_level}) { - return this._post({ - uri: `r/${this.subreddit.display_name}/wiki/settings/${this.title}`, - form: {listed, permlevel: permissionLevel} - }).return(this); - } - _modifyEditor ({name, action}) { - return this._post({ - uri: `r/${this.subreddit.display_name}/api/wiki/alloweditor/${action}`, - form: {page: this.title, username: name} - }); - } - /** - * @summary Makes the given user an approved editor of this wiki page. - * @param {object} options - * @param {string} options.name The name of the user to be added - * @returns {Promise} A Promise that fulfills with this WikiPage when the request is complete - * @example r.getSubreddit('snoowrap').getWikiPage('index').addEditor({name: 'actually_an_aardvark'}) - */ - addEditor ({name}) { - return this._modifyEditor({name, action: 'add'}).return(this); - } - /** - * @summary Revokes this user's approved editor status for this wiki page - * @param {object} options - * @param {string} options.name The name of the user to be removed - * @returns {Promise} A Promise that fulfills with this WikiPage when the request is complete - * @example r.getSubreddit('snoowrap').getWikiPage('index').removeEditor({name: 'actually_an_aardvark'}) - */ - removeEditor ({name}) { - return this._modifyEditor({name, action: 'del'}).return(this); - } - /** - * @summary Edits this wiki page, or creates it if it does not exist yet. - * @param {object} options - * @param {string} options.text The new content of the page, in markdown. - * @param {string} [options.reason] The edit reason that will appear in this page's revision history. 256 characters max - * @param {string} [options.previousRevision] Determines which revision this edit should be added to. If this parameter is - omitted, this edit is simply added to the most recent revision. - * @returns {Promise} A Promise that fulfills with this WikiPage when the request is complete - * @example r.getSubreddit('snoowrap').getWikiPage('index').edit({text: 'Welcome', reason: 'Added a welcome message'}) - */ - edit ({text, reason, previous_revision, previousRevision = previous_revision}) { - return this._post({ - uri: `r/${this.subreddit.display_name}/api/wiki/edit`, - form: {content: text, page: this.title, previous: previousRevision, reason} - }).return(this); - } - /** - * @summary Gets a list of revisions for this wiki page. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing revisions of this page - * @example - * - * r.getSubreddit('snoowrap').getRevisions({limit: 1}).then(console.log) - * // => Listing [ - * // { - * // timestamp: 1460973194, - * // reason: 'Added a welcome message', - * // author: RedditUser { name: 'not_an_aardvark', id: 'k83md', ... }, - * // page: 'index', - * // id: '506370b4-0508-11e6-b550-0e69f29e0c4d' - * // } - * // ] - */ - getRevisions (options) { - return this._getListing({uri: `r/${this.subreddit.display_name}/wiki/revisions/${this.title}`, qs: options}); - } - /** - * @summary Hides the given revision from this page's public revision history. - * @param {object} options - * @param {string} options.id The revision's id - * @returns {Promise} A Promise that fulfills with this WikiPage when the request is complete - * @example r.getSubreddit('snoowrap').getWikiPage('index').hideRevision({id: '506370b4-0508-11e6-b550-0e69f29e0c4d'}) - */ - hideRevision ({id}) { - return this._post({ - uri: `r/${this.subreddit.display_name}/api/wiki/hide`, - qs: {page: this.title, revision: id} - }).return(this); - } - /** - * @summary Reverts this wiki page to the given point. - * @param {object} options - * @param {string} options.id The id of the revision that this page should be reverted to - * @returns {Promise} A Promise that fulfills with this WikiPage when the request is complete - * @example r.getSubreddit('snoowrap').getWikiPage('index').revert({id: '506370b4-0508-11e6-b550-0e69f29e0c4d'}) - */ - revert ({id}) { - return this._post({ - uri: `r/${this.subreddit.display_name}/api/wiki/revert`, - qs: {page: this.title, revision: id} - }).return(this); - } - /** - * @summary Gets a list of discussions about this wiki page. - * @param {object} [options] Options for the resulting Listing - * @returns {Promise} A Listing containing discussions about this page - * @example - * - * r.getSubreddit('snoowrap').getWikiPage('index').getDiscussions().then(console.log) - * // => Listing [ - * // Submission { ... }, - * // Submission { ... }, - * // ... - * // ] - */ - getDiscussions (options) { - return this._getListing({uri: `r/${this.subreddit.display_name}/wiki/discussions/${this.title}`, qs: options}); - } -}; - -export default WikiPage; diff --git a/src/objects/WikiPage.ts b/src/objects/WikiPage.ts new file mode 100644 index 00000000..0fe8fcbf --- /dev/null +++ b/src/objects/WikiPage.ts @@ -0,0 +1,213 @@ +import RedditContent from './RedditContent' +import type {RedditUser, Subreddit, Listing} from './' +import type {ListingQuery} from './Listing' + + +interface Settings { + listed: boolean + permissionLevel: 0 | 1 | 2 + [key: string]: any +} + +interface EditorSettings { + action: 'add'|'del' + username: string, + [key: string]: any +} + +interface WikiSettings { + action: 'hide'|'revert', + revision: string, + [key: string]: any +} + +interface EditOptions { + content: string + reason?: string + previous?: string + [key: string]: any +} + +interface WikiPageRevision { + reason: string|null + revision_hidden: boolean + page: string + id: string + author: RedditUser + [key: string]: any +} + +interface WikiPage { + /** Custom */ + title: string + /** Custom */ + subreddit: Subreddit + content_html: string + content_md: string + may_revise: boolean + reason: string|null + revision_by: RedditUser + revision_date: number + revision_id: string +} + +/** + * A class representing a wiki page on a subreddit. + * + * **Note:** Due to a bug in reddit's CORS settings, it is not possible to fetch the contents of a wiki page on a private + * subreddit while running snoowrap in a browser. (This issue does not apply when running snoowrap in Node.js.) + * + * @example + * + * // Get a wiki page on a given subreddit by name + * r.getSubreddit('AskReddit').getWikiPage('rules') + */ +class WikiPage extends RedditContent { + static _name = 'WikiPage' + + _transformApiResponse (res: WikiPage) { + res.title = this.title + res.subreddit = this.subreddit + return res + } + get _uri () { + return `r/${this.subreddit.display_name}/wiki/${this.title}` + } + /** + * @summary Gets the current settings for this wiki page. + * @returns An Object representing the settings for this page + * @example + * + * r.getSubreddit('snoowrap').getWikiPage('index').getSettings().then(console.log) + * // => WikiPageSettings { permlevel: 0, editors: [], listed: true } + */ + getSettings () { + return this._get({url: `r/${this.subreddit.display_name}/wiki/settings/${this.title}`}) + } + /** + * @summary Edits the settings for this wiki page. + * @param options + * @param options.listed Determines whether this wiki page should appear on the public list of pages for this + * subreddit. + * @param options.permlevel Determines who should be allowed to access and edit this page `0` indicates that + * this subreddit's default wiki settings should get used, `1` indicates that only approved wiki contributors on this subreddit + * should be able to edit this page, and `2` indicates that only mods should be able to view and edit this page. + * @returns A Promise that fulfills with this WikiPage when the request is complete + * @example r.getSubreddit('snoowrap').getWikiPage('index').editSettings({listed: false, permission_level: 1}) + */ + async editSettings ({listed, permlevel, ...opts}: Settings) { + await this._post({ + url: `r/${this.subreddit.display_name}/wiki/settings/${this.title}`, + form: {...opts, listed, permlevel} + }) + return this + } + _modifyEditor ({action, username, ...opts}: EditorSettings) { + return this._post({ + url: `r/${this.subreddit.display_name}/api/wiki/alloweditor/${action}`, + form: {...opts, page: this.title, username} + }) + } + /** + * @summary Makes the given user an approved editor of this wiki page. + * @param username The name of the user to be added + * @returns A Promise that fulfills with this WikiPage when the request is complete + * @example r.getSubreddit('snoowrap').getWikiPage('index').addEditor({name: 'actually_an_aardvark'}) + */ + async addEditor (username: string) { + await this._modifyEditor({username, action: 'add'}) + return this + } + /** + * @summary Revokes this user's approved editor status for this wiki page + * @param username The name of the user to be removed + * @returns A Promise that fulfills with this WikiPage when the request is complete + * @example r.getSubreddit('snoowrap').getWikiPage('index').removeEditor({name: 'actually_an_aardvark'}) + */ + async removeEditor (username: string) { + await this._modifyEditor({username, action: 'del'}) + return this + } + /** + * @summary Edits this wiki page, or creates it if it does not exist yet. + * @param options + * @param options.text The new content of the page, in markdown. + * @param {string} [options.reason] The edit reason that will appear in this page's revision history. 256 characters max + * @param {string} [options.previousRevision] Determines which revision this edit should be added to. If this parameter is + * omitted, this edit is simply added to the most recent revision. + * @returns A Promise that fulfills with this WikiPage when the request is complete + * @example r.getSubreddit('snoowrap').getWikiPage('index').edit({text: 'Welcome', reason: 'Added a welcome message'}) + */ + async edit ({content, reason, previous, ...opts}: EditOptions) { + await this._post({ + url: `r/${this.subreddit.display_name}/api/wiki/edit`, + form: {...opts, content, page: this.title, previous, reason} + }) + return this + } + /** + * @summary Gets a list of revisions for this wiki page. + * @param options Options for the resulting Listing + * @returns A Listing containing revisions of this page + * @example + * + * r.getSubreddit('snoowrap').getRevisions({limit: 1}).then(console.log) + * // => Listing [ + * // { + * // timestamp: 1460973194, + * // reason: 'Added a welcome message', + * // author: RedditUser { name: 'not_an_aardvark', id: 'k83md', ... }, + * // page: 'index', + * // id: '506370b4-0508-11e6-b550-0e69f29e0c4d' + * // } + * // ] + */ + getRevisions (options: ListingQuery): Promise> { + return this._getListing({uri: `r/${this.subreddit.display_name}/wiki/revisions/${this.title}`, qs: options}) + } + _modifyWiki ({action, revision, ...opts}: WikiSettings) { + return this._post({ + url: `r/${this.subreddit.display_name}/api/wiki/${action}`, + params: {...opts, page: this.title, revision} + }) + } + /** + * @summary Hides the given revision from this page's public revision history. + * @param revision The revision's id + * @returns A Promise that fulfills with this WikiPage when the request is complete + * @example r.getSubreddit('snoowrap').getWikiPage('index').hideRevision({id: '506370b4-0508-11e6-b550-0e69f29e0c4d'}) + */ + async hideRevision (revision: string) { + await this._modifyWiki({action: 'hide', revision}) + return this + } + /** + * @summary Reverts this wiki page to the given point. + * @param revision The id of the revision that this page should be reverted to + * @returns A Promise that fulfills with this WikiPage when the request is complete + * @example r.getSubreddit('snoowrap').getWikiPage('index').revert({id: '506370b4-0508-11e6-b550-0e69f29e0c4d'}) + */ + async revert (revision: string) { + await this._modifyWiki({action: 'revert', revision}) + return this + } + /** + * @summary Gets a list of discussions about this wiki page. + * @param options Options for the resulting Listing + * @returns A Listing containing discussions about this page + * @example + * + * r.getSubreddit('snoowrap').getWikiPage('index').getDiscussions().then(console.log) + * // => Listing [ + * // Submission { ... }, + * // Submission { ... }, + * // ... + * // ] + */ + getDiscussions (options?: ListingQuery) { + return this._getListing({uri: `r/${this.subreddit.display_name}/wiki/discussions/${this.title}`, qs: options}) + } +} + +export default WikiPage +export {Settings, EditorSettings, WikiSettings, EditOptions, WikiPageRevision} diff --git a/src/objects/index.d.ts b/src/objects/index.d.ts deleted file mode 100644 index ea8c196d..00000000 --- a/src/objects/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -export { default as Comment } from './Comment'; -export { default as Listing, ListingOptions, SortedListingOptions } from './Listing'; -export { default as LiveThread, LiveThreadSettings } from './LiveThread'; -export { default as ModmailConversation } from "./ModmailConversation"; -export { default as MultiReddit, MultiRedditProperties } from './MultiReddit'; -export { default as PrivateMessage } from './PrivateMessage'; -export { default as RedditContent } from './RedditContent'; -export { default as RedditUser } from './RedditUser'; -export { default as ReplyableContent } from './ReplyableContent'; -export { default as Submission } from './Submission'; -export { default as Subreddit, SubredditSettings } from './Subreddit'; -export { default as VoteableContent } from './VoteableContent'; -export { default as WikiPage } from './WikiPage'; \ No newline at end of file diff --git a/src/objects/index.js b/src/objects/index.js deleted file mode 100644 index 8d6e7075..00000000 --- a/src/objects/index.js +++ /dev/null @@ -1,15 +0,0 @@ -export {default as RedditContent} from './RedditContent.js'; -export {default as ReplyableContent} from './ReplyableContent.js'; -export {default as VoteableContent} from './VoteableContent.js'; -export {default as Comment} from './Comment.js'; -export {default as RedditUser} from './RedditUser.js'; -export {default as Submission} from './Submission.js'; -export {default as LiveThread} from './LiveThread.js'; -export {default as PrivateMessage} from './PrivateMessage.js'; -export {default as Subreddit} from './Subreddit.js'; -export {default as MultiReddit} from './MultiReddit.js'; -export {default as WikiPage} from './WikiPage.js'; -export {default as Listing} from './Listing.js'; -export {default as More} from './More.js'; -export {default as UserList} from './UserList.js'; -export {default as ModmailConversation} from './ModmailConversation.js'; diff --git a/src/objects/index.ts b/src/objects/index.ts new file mode 100644 index 00000000..41736d7a --- /dev/null +++ b/src/objects/index.ts @@ -0,0 +1,15 @@ +export {default as Listing} from './Listing' +export {default as More} from './More' +export {default as RedditContent} from './RedditContent' +export {default as ReplyableContent} from './ReplyableContent' +export {default as VoteableContent} from './VoteableContent' +export {default as Comment} from './Comment' +export {default as RedditUser} from './RedditUser' +export {default as Submission} from './Submission' +export {default as LiveThread} from './LiveThread' +export {default as PrivateMessage} from './PrivateMessage' +export {default as Subreddit} from './Subreddit' +export {default as MultiReddit} from './MultiReddit' +export {default as WikiPage} from './WikiPage' +export {default as UserList} from './UserList' +export {default as ModmailConversation} from './ModmailConversation' diff --git a/src/request_handler.js b/src/request_handler.js deleted file mode 100644 index f1a6ffe6..00000000 --- a/src/request_handler.js +++ /dev/null @@ -1,292 +0,0 @@ -import {includes, merge} from 'lodash'; -import Promise from './Promise.js'; -import {IDEMPOTENT_HTTP_VERBS, MAX_TOKEN_LATENCY} from './constants.js'; -import {rateLimitWarning, RateLimitError} from './errors.js'; - -/** -* @summary Sends an oauth-authenticated request to the reddit server, and returns the server's response. -* @desc **Note**: While this function primarily exists for internal use, it is exposed and considered a stable feature. -However, keep in mind that there are usually better alternatives to using this function. For instance, this -function can be used to send a POST request to the 'api/vote' endpoint in order to upvote a comment, but it's generally -easier to just use snoowrap's [upvote function]{@link VoteableContent#upvote}. -* -* If you're using this function to access an API feature/endpoint that is unsupported by snoowrap, please consider [creating an -issue for it](https://github.com/not-an-aardvark/snoowrap/issues) so that the functionality can be added to snoowrap more -directly. -* @param {object} options Options for the request. For documentation on these options, see the -[Request API](https://www.npmjs.com/package/request). Supported options include `uri`, `qs`, `form`, `headers`, `method`, -`auth`, and `body`. A default `baseUrl` parameter of `this.config().endpoint_domain` is internally included by default, so it -is recommended that a `uri` parameter be used, rather than a `url` parameter with a -domain name. -* @returns {Promise} A Promise that fulfills with reddit's response. -* @memberof snoowrap -* @instance -* @example -* -* r.oauthRequest({uri: '/user/spez/about', method: 'get'}).then(console.log) -* // => RedditUser { name: 'spez', link_karma: 9567, ... } -* -* // Note that this is equivalent to: -* r.getUser('spez').fetch().then(console.log) -* -* // ###### -* -* r.oauthRequest({uri: '/api/vote', method: 'post', form: {dir: 1, id: 't3_4fzg2k'}}) -* // equivalent to: -* r.getSubmission('4fzg2k').upvote() -* -* // ###### -* -* r.oauthRequest({uri: '/top', method: 'get', qs: {t: 'all'}}) -* // equivalent to: -* r.getTop({time: 'all'}) -*/ -export function oauthRequest (options, attempts = 1) { - return Promise.resolve() - .then(() => this._awaitRatelimit()) - .then(() => this._awaitRequestDelay()) - .then(() => _awaitExponentialBackoff(attempts)) - .then(() => this.updateAccessToken()) - .then(token => { - return this.rawRequest(merge({ - json: true, - headers: {'user-agent': this.userAgent}, - baseUrl: `https://oauth.${this._config.endpointDomain}`, - qs: {raw_json: 1}, - auth: {bearer: token}, - resolveWithFullResponse: true, - timeout: this._config.requestTimeout, - transform: (body, response) => { - if (Object.prototype.hasOwnProperty.call(response.headers, 'x-ratelimit-remaining')) { - this.ratelimitRemaining = +response.headers['x-ratelimit-remaining']; - this.ratelimitExpiration = Date.now() + (response.headers['x-ratelimit-reset'] * 1000); - } - this._debug( - `Received a ${response.statusCode} status code from a \`${response.request.method}\` request`, - `sent to ${response.request.uri.href}. ratelimitRemaining: ${this.ratelimitRemaining}` - ); - return response; - } - }, options)); - }).then(response => { - const populated = this._populate(response.body); - if (populated && populated.constructor._name === 'Listing') { - populated._setUri(response.request.uri.href); - } - return populated; - }).catch(...this._config.retryErrorCodes.map(retryCode => ({statusCode: retryCode})), e => { - if (!includes(IDEMPOTENT_HTTP_VERBS, e.response.request.method) || attempts >= this._config.maxRetryAttempts) { - throw e; - } - /* If the error's status code is in the user's configured `retryStatusCodes` and this request still has attempts - remaining, retry this request and increment the `attempts` counter. */ - this._warn( - `Received status code ${e.statusCode} from reddit.`, - `Retrying request (attempt ${attempts + 1}/${this._config.maxRetryAttempts})...` - ); - return this.oauthRequest(options, attempts + 1); - }).catch({statusCode: 401}, e => { - /* If the server returns a 401 error, it's possible that the access token expired during the latency period as this - request was being sent. In this scenario, snoowrap thought that the access token was valid for a few more seconds, so it - didn't refresh the token, but the token had expired by the time the request reached the server. To handle this issue, - invalidate the access token and call oauth_request again, automatically causing the token to be refreshed. */ - if (this.accessToken && this.tokenExpiration - Date.now() < MAX_TOKEN_LATENCY) { - this.accessToken = null; - this.tokenExpiration = null; - return this.oauthRequest(options, attempts); - } - throw e; - }); -} - -export function _awaitExponentialBackoff (attempts) { - if (attempts === 1) { - return Promise.resolve(); - } - - return Promise.delay((Math.pow(2, attempts - 1) + (Math.random() - 0.3)) * 1000); -} - -export function _awaitRatelimit () { - if (this.ratelimitRemaining < 1 && Date.now() < this.ratelimitExpiration) { - // If the ratelimit has been exceeded, delay or abort the request depending on the user's config. - const timeUntilExpiry = this.ratelimitExpiration - Date.now(); - if (this._config.continueAfterRatelimitError) { - /* If the `continue_after_ratelimit_error` setting is enabled, queue the request, wait until the next ratelimit - period, and then send it. */ - this._warn(rateLimitWarning(timeUntilExpiry)); - return Promise.delay(timeUntilExpiry); - } - // Otherwise, throw an error. - throw new RateLimitError(timeUntilExpiry); - } - // If the ratelimit hasn't been exceeded, no delay is necessary. - return Promise.resolve(); -} - -export function _awaitRequestDelay () { - const now = Date.now(); - const waitTime = this._nextRequestTimestamp - now; - this._nextRequestTimestamp = Math.max(now, this._nextRequestTimestamp) + this._config.requestDelay; - return Promise.delay(waitTime); -} - -/** -* @summary Sends a request to the reddit server, authenticated with the user's client ID and client secret. -* @desc **Note**: This is used internally as part of the authentication process, but it cannot be used to actually fetch -content from reddit. To do that, use {@link snoowrap#oauthRequest} or another of snoowrap's helper functions. -* -* This function can work with alternate `this`-bindings, provided that the binding has the `clientId`, `clientSecret`, and -`userAgent` properties. This allows it be used if no snoowrap requester has been created yet. -* @param {object|string} options Options for the request; these are passed directly to the -[Request API](https://www.npmjs.com/package/request). -* @returns {Promise} The response from the reddit server -* @example -* -* // example: this function could be used to exchange a one-time authentication code for a refresh token. -snoowrap.prototype.credentialedClientRequest.call({ - clientId: 'client id goes here', - clientSecret: 'client secret goes here', - userAgent: 'user agent goes here' -}, { - method: 'post', - baseUrl: 'https://www.reddit.com', - uri: 'api/v1/access_token', - form: {grant_type: 'authorization_code', code: 'code goes here', redirect_uri: 'redirect uri goes here'} -}).then(response => { - //handle response here -}) -* @memberof snoowrap -* @instance -*/ -export function credentialedClientRequest (options) { - const requestFunc = this.rawRequest || rawRequest; - return Promise.resolve(requestFunc.call(this, merge({ - json: true, - auth: {user: this.clientId || this.client_id || '', pass: this.clientSecret || this.client_secret || ''}, - headers: {'user-agent': this.userAgent}, - baseUrl: this._config ? `https://www.${this._config.endpointDomain}` : undefined - }, options))); -} - -/** -* @summary Sends a request to the reddit server without authentication. -* @param {object|string} options Options for the request; these are passed directly to the -[Request API](https://www.npmjs.com/package/request). -* @returns {Promise} The response from the reddit server -* @memberof snoowrap -* @instance -*/ -export function unauthenticatedRequest (options) { - return Promise.resolve(this.rawRequest(merge({ - json: true, - headers: {'user-agent': this.userAgent}, - baseUrl: `https://www.${this._config.endpointDomain}` - }, options))); -} - -/** -* @summary Updates this requester's access token if the current one is absent or expired. -* @desc **Note**: This function is automatically called internally when making a request. While the function is exposed as -a stable feature, using it is rarely necessary unless an access token is needed for some external purpose. -* @returns {Promise} A Promise that fulfills with the access token when this request is complete -* @memberof snoowrap -* @instance -* @example r.updateAccessToken() -*/ -export function updateAccessToken () { - // If the current access token is missing or expired, and it is possible to get a new one, do so. - if ((!this.accessToken || Date.now() > this.tokenExpiration) && (this.refreshToken || (this.username && this.password))) { - return this.credentialedClientRequest({ - method: 'post', - uri: 'api/v1/access_token', - form: this.refreshToken - ? {grant_type: 'refresh_token', refresh_token: this.refreshToken} - : {grant_type: 'password', username: this.username, password: this.password} - }).then(tokenInfo => { - this.accessToken = tokenInfo.access_token; - this.tokenExpiration = Date.now() + (tokenInfo.expires_in * 1000); - if (tokenInfo.error === 'invalid_grant') { - throw new Error('"Invalid grant" error returned from reddit. (You might have incorrect credentials.)'); - } else if (tokenInfo.error_description !== undefined) { - throw new Error(`Reddit returned an error: ${tokenInfo.error}: ${tokenInfo.error_description}`); - } else if (tokenInfo.error !== undefined) { - throw new Error(`Reddit returned an error: ${tokenInfo.error}`); - } - this.scope = tokenInfo.scope.split(' '); - return this.accessToken; - }); - } - // Otherwise, just return the existing token. - return Promise.resolve(this.accessToken); -} - -/** -* @function -* @name rawRequest -* @summary Sends an HTTP request -* @desc **Note**: This function is called internally whenever snoowrap makes a request. You generally should not call this -* function directly; use {@link snoowrap#oauthRequest} or another snoowrap function instead. -* -* This method allows snoowrap's request behavior to be customized via subclassing. If you create a snoowrap subclass and shadow -* this method, all requests from snoowrap will pass through it. -* -* To ensure that all other snoowrap methods work correctly, the API for a shadowed version of this method must match the API for -* the original `makeRequest` method. This method is based on the API of the -* [request-promise](https://www.npmjs.com/package/request-promise) library, so if you do create a subclass, it might be helpful -* to use `request-promise` internally. This will ensure that the API works correctly, so that you don't have to reimplement this -* function's API from scratch. -* -* @param {object} options Options for the request -* @param {boolean} options.json If `true`, the `Content-Type: application/json` header is added, and the response body will be -* parsed as JSON automatically. -* @param {string} options.baseUrl The base URL that a request should be sent to -* @param {string} options.uri The uri that a request should be sent to, using the provided `baseUrl`. -* @param {string} options.method='GET' Method for the request -* @param {object} options.headers Headers for the request -* @param {object} [options.qs] Querystring parameters for the request -* @param {object} [options.form] Form data for the request. If provided, the `Content-Type: application/x-www-form-urlencoded` -* header is set, and the provided object is serialized into URL-encoded form data in the request body. -* @param {object} [options.formData] Multipart form data for the request. If provided, the `Content-Type: multipart/form-data` -* header is set, and the provided object is serialized as multipart form data. -* @param {object} [options.body] The body of the request. Should be converted to a string with JSON.stringify(). This is ignored -* for GET requests, or of `options.form` or `options.formData` are provided. -* @param {Function} [options.transform] A function that is called before the response Promise fulfills. Accepts two parameters: -* `response.body` and `response`. This function should be called regardless of the status code of the response, and the returned -* Promise from `makeRequest` should fulfill with its return value. -* @param {boolean} [options.resolveWithFullResponse=false] If `true`, a Promise for the entire response is returned. If `false`, -* a Promise for only the response body is returned. This is ignored if an `options.transform` function is provided. -* @returns {Promise} A Promise for a response object. Depending on `options.transform` and `options.resolveWithFullResponse`, -* the Promise should settle with either the response object itself, the body of the response, or the value returned by -* `options.transform`. The Promise should be fulfilled if the status code is between 200 and 299, inclusive, and reject -* otherwise. (If a redirect is returned from the server, the function should follow the redirect if possible, otherwise reject -* with an error.) A response object has 4 properties: `statusCode` (number) the status code of the response, `body` (object) -* the body of the response, `headers` (object) the parsed response headers, and `request` (object) an object of the form -* `{method: 'GET', uri: {href: 'https://oauth.reddit.com/full/url'}}` representing information about the original request. -* @memberof snoowrap -* @instance -* @example -* -* const snoowrap = require('snoowrap'); -* -* class SnoowrapSubclass extends snoowrap { -* rawRequest(options) { -* // do custom behavior with `options` if you want, then call the regular rawRequest function -* console.log(`made a request with options:`); -* console.log(options); -* return super.rawRequest(options) -* } -* } -* -* const request = require('request-promise'); -* -* class AnotherSnoowrapSubclass extends snoowrap { -* rawRequest(options) { -* // send all requests through a proxy -* return request(Object.assign(options, {proxy: 'https://example.com'})) -* } -* } -*/ -export const rawRequest = typeof XMLHttpRequest !== 'undefined' - ? require('./xhr') - : require('request-promise').defaults({gzip: true}); diff --git a/src/snoowrap.d.ts b/src/snoowrap-old.d.ts.txt similarity index 99% rename from src/snoowrap.d.ts rename to src/snoowrap-old.d.ts.txt index a9d94658..c2571693 100644 --- a/src/snoowrap.d.ts +++ b/src/snoowrap-old.d.ts.txt @@ -1,3 +1,4 @@ +// @ts-nocheck // Thanks to these people for type definitions: // Vito Samson // TheAppleFreak diff --git a/src/snoowrap.js b/src/snoowrap-old.js.txt similarity index 55% rename from src/snoowrap.js rename to src/snoowrap-old.js.txt index fd8a58d1..59ff1d59 100644 --- a/src/snoowrap.js +++ b/src/snoowrap-old.js.txt @@ -1,10 +1,11 @@ import {defaults, forOwn, includes, isEmpty, map, mapValues, omit, omitBy, snakeCase, values} from 'lodash'; -import Promise from './Promise.js'; -import promiseWrap from 'promise-chains'; import util from 'util'; +import path from 'path'; +import stream from 'stream'; +import {createReadStream} from 'fs'; import * as requestHandler from './request_handler.js'; -import {HTTP_VERBS, KINDS, MAX_LISTING_ITEMS, MODULE_NAME, USER_KEYS, SUBREDDIT_KEYS, VERSION} from './constants.js'; -import * as errors from './errors.js'; +import {HTTP_VERBS, KINDS, MAX_LISTING_ITEMS, MODULE_NAME, USER_KEYS, SUBREDDIT_KEYS, VERSION, MIME_TYPES, SUBMISSION_ID_REGEX, MEDIA_TYPES, PLACEHOLDER_REGEX} from './constants'; +import * as errors from './errors'; import { addEmptyRepliesListing, addFullnamePrefix, @@ -14,61 +15,71 @@ import { isBrowser, requiredArg } from './helpers.js'; -import createConfig from './create_config.js'; +import defaultConfig from './defaultConfig'; import * as objects from './objects/index.js'; +/* eslint-disable-next-line import/no-unresolved */ +import MediaFile, {MediaImg, MediaVideo, MediaGif} from './objects/MediaFile'; + +const fetch = global.fetch; +const Blob = global.Blob; +const FormData = isBrowser ? global.FormData : require('form-data'); +const WebSocket = isBrowser ? global.WebSocket : require('ws'); const api_type = 'json'; -/** The class for a snoowrap requester. +/** + * The class for a snoowrap requester. * A requester is the base object that is used to fetch content from reddit. Each requester contains a single set of OAuth - tokens. - - If constructed with a refresh token, a requester will be able to repeatedly generate access tokens as necessary, without any - further user intervention. After making at least one request, a requester will have the `access_token` property, which specifies - the access token currently in use. It will also have a few additional properties such as `scope` (an array of scope strings) - and `ratelimitRemaining` (the number of requests remaining for the current 10-minute interval, in compliance with reddit's - [API rules](https://github.com/reddit/reddit/wiki/API).) These properties primarily exist for internal use, but they are - exposed since they are useful externally as well. + * tokens. + * + * If constructed with a refresh token, a requester will be able to repeatedly generate access tokens as necessary, without any + * further user intervention. After making at least one request, a requester will have the `accessToken` property, which specifies + * the access token currently in use. It will also have a few additional properties such as `scope` (an array of scope strings) + * and `ratelimitRemaining` (the number of requests remaining for the current 10-minute interval, in compliance with reddit's + * [API rules](https://github.com/reddit/reddit/wiki/API).) These properties primarily exist for internal use, but they are + * exposed since they are useful externally as well. */ const snoowrap = class snoowrap { /** * @summary Constructs a new requester. * @desc You should use the snoowrap constructor if you are able to authorize a reddit account in advance (e.g. for a Node.js - script that always uses the same account). If you aren't able to authorize in advance (e.g. acting through an arbitrary user's - account while running snoowrap in a browser), then you should use {@link snoowrap.getAuthUrl} and - {@link snoowrap.fromAuthCode} instead. + * script that always uses the same account). If you aren't able to authorize in advance (e.g. acting through an arbitrary user's + * account while running snoowrap in a browser), then you should use {@link snoowrap.getAuthUrl} and + * {@link snoowrap.fromAuthCode} instead. * * To edit snoowrap specific settings, see {@link snoowrap#config}. * * snoowrap supports several different options for pre-existing authentication: * 1. *Refresh token*: To authenticate with a refresh token, pass an object with the properties `userAgent`, `clientId`, - `clientSecret`, and `refreshToken` to the snoowrap constructor. You will need to get the refresh token from reddit - beforehand. A script to automatically generate refresh tokens for you can be found - [here](https://github.com/not-an-aardvark/reddit-oauth-helper). + * `clientSecret`, and `refreshToken` to the snoowrap constructor. You will need to get the refresh token from reddit + * beforehand. A script to automatically generate refresh tokens for you can be found + * [here](https://github.com/not-an-aardvark/reddit-oauth-helper). * 1. *Username/password*: To authenticate with a username and password, pass an object with the properties `userAgent`, - `clientId`, `clientSecret`, `username`, and `password` to the snoowrap constructor. Note that username/password - authentication is only possible for `script`-type apps. + * `clientId`, `clientSecret`, `username`, and `password` to the snoowrap constructor. Note that username/password + * authentication is only possible for `script`-type apps. * 1. *Access token*: To authenticate with an access token, pass an object with the properties `userAgent` and `accessToken` - to the snoowrap constructor. Note that all access tokens expire one hour after being generated, so this method is - not recommended for long-term use. + * to the snoowrap constructor. Note that all access tokens expire one hour after being generated, so this method is + * not recommended for long-term use. * @param {object} options An object containing authentication options. This should always have the property `userAgent`. It - must also contain some combination of credentials (see above) + * must also contain some combination of credentials (see above) * @param {string} options.userAgent A unique description of what your app does. This argument is not necessary when snoowrap - is running in a browser. + * is running in a browser. * @param {string} [options.clientId] The client ID of your app (assigned by reddit) * @param {string} [options.clientSecret] The client secret of your app (assigned by reddit). If you are using a refresh token - with an installed app (which does not have a client secret), pass an empty string as your `clientSecret`. + * with an installed app (which does not have a client secret), pass an empty string as your `clientSecret`. * @param {string} [options.username] The username of the account to access * @param {string} [options.password] The password of the account to access * @param {string} [options.refreshToken] A refresh token for your app * @param {string} [options.accessToken] An access token for your app */ constructor ({ - // The function signature for the constructor is a bit large due to the snake_case aliases. Essentially, it accepts an - // object with properties {userAgent, clientId, clientSecret, refreshToken, accessToken, username, password}. - // Additionally, if snake_case properties are provided and camelCase properties are not (e.g. `user_agent` is provided but - // `userAgent` is not), then the `userAgent` identifier gets set to the provided `user_agent` property. This is needed for - // backwards compatibility; snoowrap previously only accepted snake_case props, but now it also accepts camelCase props. + /** + * The function signature for the constructor is a bit large due to the snake_case aliases. Essentially, it accepts an + * object with properties {userAgent, clientId, clientSecret, refreshToken, accessToken, username, password}. + * Additionally, if snake_case properties are provided and camelCase properties are not (e.g. `user_agent` is provided but + * `userAgent` is not), then the `userAgent` identifier gets set to the provided `user_agent` property. This is needed for + * backwards compatibility; snoowrap previously only accepted snake_case props, but now it also accepts camelCase props. + */ user_agent, userAgent = user_agent, client_id, clientId = client_id, client_secret, clientSecret = client_secret, @@ -100,7 +111,7 @@ const snoowrap = class snoowrap { ratelimitExpiration: null, tokenExpiration: null, scope: null, - _config: createConfig(), + _config: {...defaultConfig}, _nextRequestTimestamp: -Infinity }); addSnakeCaseShadowProps(this); @@ -109,31 +120,32 @@ const snoowrap = class snoowrap { /** * @summary Gets an authorization URL, which allows a user to authorize access to their account * @desc This create a URL where a user can authorize an app to act through their account. If the user visits the returned URL - in a web browser, they will see a page that looks like [this](https://i.gyazo.com/0325534f38b78c1dbd4c84d690dda6c2.png). If - the user clicks "Allow", they will be redirected to your `redirectUri`, with a `code` querystring parameter containing an + * in a web browser, they will see a page that looks like [this](https://i.gyazo.com/0325534f38b78c1dbd4c84d690dda6c2.png). If + * the user clicks "Allow", they will be redirected to your `redirectUri`, with a `code` querystring parameter containing an * *authorization code*. If this code is passed to {@link snoowrap.fromAuthCode}, you can create a requester to make - requests on behalf of the user. + * requests on behalf of the user. * * The main use-case here is for running snoowrap in a browser. You can generate a URL, send the user there, and then continue - after the user authenticates on reddit and is redirected back. + * after the user authenticates on reddit and is redirected back. * * @param {object} options * @param {string} options.clientId The client ID of your app (assigned by reddit). If your code is running clientside in a - browser, using an "Installed" app type is recommended. - * @param {string[]} options.scope An array of scopes (permissions on the user's account) to request on the authentication - page. A list of possible scopes can be found [here](https://www.reddit.com/api/v1/scopes). You can also get them on-the-fly - with {@link snoowrap#getOauthScopeList}. + * browser, using an "Installed" app type is recommended. + * @param {string[]} [options.scope=['*']] An array of scopes (permissions on the user's account) to request on the authentication + * page. A list of possible scopes can be found [here](https://www.reddit.com/api/v1/scopes). You can also get them on-the-fly + * with {@link snoowrap#getOauthScopeList}. Passing an array with a single asterisk `['*']` gives you full scope. * @param {string} options.redirectUri The URL where the user should be redirected after authenticating. This **must** be the - same as the redirect URI that is configured for the reddit app. (If there is a mismatch, the returned URL will display an - error page instead of an authentication form.) + * same as the redirect URI that is configured for the reddit app. (If there is a mismatch, the returned URL will display an + * error page instead of an authentication form.) * @param {boolean} [options.permanent=true] If `true`, the app will have indefinite access to the user's account. If `false`, - access to the user's account will expire after 1 hour. + * access to the user's account will expire after 1 hour. * @param {string} [options.state] A string that can be used to verify a user after they are redirected back to the site. When - the user is redirected from reddit, to the redirect URI after authenticating, the resulting URI will have this same `state` - value in the querystring. (See [here](http://www.twobotechnologies.com/blog/2014/02/importance-of-state-in-oauth2.html) for - more information on how to use the `state` value.) + * the user is redirected from reddit, to the redirect URI after authenticating, the resulting URI will have this same `state` + * value in the querystring. (See [here](http://www.twobotechnologies.com/blog/2014/02/importance-of-state-in-oauth2.html) for + * more information on how to use the `state` value.) * @param {string} [options.endpointDomain='reddit.com'] The endpoint domain for the URL. If the user is authenticating on - reddit.com (as opposed to some other site with a reddit-like API), you can omit this value. + * reddit.com (as opposed to some other site with a reddit-like API), you can omit this value. + * @param {boolean} [options.compact=false] If `true`, the mobile version of the authorization URL will be used instead. * @returns {string} A URL where the user can authenticate with the given options * @example * @@ -150,18 +162,20 @@ const snoowrap = class snoowrap { */ static getAuthUrl ({ clientId = requiredArg('clientId'), - scope = requiredArg('scope'), + scope = ['*'], redirectUri = requiredArg('redirectUri'), permanent = true, state = '_', - endpointDomain = 'reddit.com' + endpointDomain = 'reddit.com', + compact = false }) { if (!(Array.isArray(scope) && scope.length && scope.every(scopeValue => scopeValue && typeof scopeValue === 'string'))) { throw new TypeError('Missing `scope` argument; a non-empty list of OAuth scopes must be provided'); } return ` - https://www.${endpointDomain}/api/v1/authorize? - client_id=${encodeURIComponent(clientId)} + https://www.${endpointDomain}/api/v1/authorize + ${compact ? '.compact' : ''} + ?client_id=${encodeURIComponent(clientId)} &response_type=code &state=${encodeURIComponent(state)} &redirect_uri=${encodeURIComponent(redirectUri)} @@ -173,23 +187,23 @@ const snoowrap = class snoowrap { /** * @summary Creates a snoowrap requester from an authorization code. * @desc An authorization code is the `code` value that appears in the querystring after a user authenticates with reddit and - is redirected. For more information, see {@link snoowrap.getAuthUrl}. + * is redirected. For more information, see {@link snoowrap.getAuthUrl}. * * The main use-case for this function is for running snoowrap in a browser. You can generate a URL with - {@link snoowrap.getAuthUrl} and send the user to that URL, and then use this function to create a requester when - the user is redirected back with an authorization code. + * {@link snoowrap.getAuthUrl} and send the user to that URL, and then use this function to create a requester when + * the user is redirected back with an authorization code. * @param {object} options * @param {string} options.code The authorization code * @param {string} options.userAgent A unique description of what your app does. This argument is not necessary when snoowrap - is running in a browser. + * is running in a browser. * @param {string} options.clientId The client ID of your app (assigned by reddit). If your code is running clientside in a - browser, using an "Installed" app type is recommended. + * browser, using an "Installed" app type is recommended. * @param {string} [options.clientSecret] The client secret of your app. If your app has the "Installed" app type, omit - this parameter. + * this parameter. * @param {string} options.redirectUri The redirect URI that is configured for the reddit app. * @param {string} [options.endpointDomain='reddit.com'] The endpoint domain that the returned requester should be configured - to use. If the user is authenticating on reddit.com (as opposed to some other site with a reddit-like API), you can omit this - value. + * to use. If the user is authenticating on reddit.com (as opposed to some other site with a reddit-like API), you can omit this + * value. * @returns {Promise} A Promise that fulfills with a `snoowrap` instance * @example * @@ -208,7 +222,7 @@ const snoowrap = class snoowrap { * }); * }) */ - static fromAuthCode ({ + static async fromAuthCode ({ code = requiredArg('code'), userAgent = isBrowser ? global.navigator.userAgent : requiredArg('userAgent'), clientId = requiredArg('clientId'), @@ -216,7 +230,7 @@ const snoowrap = class snoowrap { redirectUri = requiredArg('redirectUri'), endpointDomain = 'reddit.com' }) { - return this.prototype.credentialedClientRequest.call({ + const response = await this.prototype.credentialedClientRequest.call({ userAgent, clientId, clientSecret, @@ -224,18 +238,19 @@ const snoowrap = class snoowrap { rawRequest: this.prototype.rawRequest }, { method: 'post', - baseUrl: `https://www.${endpointDomain}/`, - uri: 'api/v1/access_token', + baseURL: `https://www.${endpointDomain}/`, + url: 'api/v1/access_token', form: {grant_type: 'authorization_code', code, redirect_uri: redirectUri} - }).then(response => { - if (response.error) { - throw new errors.RequestError(`API Error: ${response.error} - ${response.error_description}`); - } - // Use `new this` instead of `new snoowrap` to ensure that subclass instances can be returned - const requester = new this({userAgent, clientId, clientSecret, ...response}); - requester.config({endpointDomain}); - return requester; }); + if (response.data.error) { + throw new errors.RequestError(`API Error: ${response.data.error} - ${response.data.error_description}`); + } + // Use `new this` instead of `new snoowrap` to ensure that subclass instances can be returned + const requester = new this({userAgent, clientId, clientSecret, ...response.data}); + requester.tokenExpiration = Date.now() + (response.data.expires_in * 1000); + requester.scope = response.data.scope.split(' '); + requester.config({endpointDomain}); + return requester; } /** @@ -259,7 +274,7 @@ const snoowrap = class snoowrap { * the app-type and its use case. * @param {object} options * @param {string} options.userAgent A unique description of what your app does. This argument is not necessary when snoowrap - is running in a browser. + * is running in a browser. * @param {string} options.clientId The client ID of your app (assigned by reddit). If your code is running clientside in a * browser, using an "Installed" app type is recommended. * @param {string} [options.clientSecret] The client secret of your app. Only required for "client_credentials" grant type. @@ -270,10 +285,10 @@ const snoowrap = class snoowrap { * @param {string} [options.grantType=snoowrap.grantType.INSTALLED_CLIENT] The type of "user-less" * token to use {@link snoowrap.grantType} * @param {boolean} [options.permanent=true] If `true`, the app will have indefinite access. If `false`, - access will expire after 1 hour. + * access will expire after 1 hour. * @param {string} [options.endpointDomain='reddit.com'] The endpoint domain that the returned requester should be configured - to use. If the user is authenticating on reddit.com (as opposed to some other site with a reddit-like API), you can omit this - value. + * to use. If the user is authenticating on reddit.com (as opposed to some other site with a reddit-like API), you can omit this + * value. * @returns {Promise} A Promise that fulfills with a `snoowrap` instance * @example * @@ -301,7 +316,7 @@ const snoowrap = class snoowrap { * }); * }) */ - static fromApplicationOnlyAuth ({ + static async fromApplicationOnlyAuth ({ userAgent = isBrowser ? global.navigator.userAgent : requiredArg('userAgent'), clientId = requiredArg('clientId'), clientSecret, @@ -310,64 +325,62 @@ const snoowrap = class snoowrap { permanent = true, endpointDomain = 'reddit.com' }) { - return this.prototype.credentialedClientRequest.call({ + const response = await this.prototype.credentialedClientRequest.call({ clientId, clientSecret, // Use `this.prototype.rawRequest` function to allow for custom `rawRequest` method usage in subclasses. rawRequest: this.prototype.rawRequest }, { method: 'post', - baseUrl: `https://www.${endpointDomain}/`, - uri: 'api/v1/access_token', + baseURL: `https://www.${endpointDomain}/`, + url: 'api/v1/access_token', form: {grant_type: grantType, device_id: deviceId, duration: permanent ? 'permanent' : 'temporary'} - }).then(response => { - if (response.error) { - throw new errors.RequestError(`API Error: ${response.error} - ${response.error_description}`); - } - // Use `new this` instead of `new snoowrap` to ensure that subclass instances can be returned - const requester = new this({userAgent, clientId, clientSecret, ...response}); - requester.config({endpointDomain}); - return requester; }); - } - _newObject (objectType, content, _hasFetched = false) { - return Array.isArray(content) ? content : new snoowrap.objects[objectType](content, this, _hasFetched); + if (response.data.error) { + throw new errors.RequestError(`API Error: ${response.data.error} - ${response.data.error_description}`); + } + // Use `new this` instead of `new snoowrap` to ensure that subclass instances can be returned + const requester = new this({userAgent, clientId, clientSecret, ...response.data}); + requester.tokenExpiration = Date.now() + (response.data.expires_in * 1000); + requester.scope = response.data.scope.split(' '); + requester.config({endpointDomain}); + return requester; } /** * @summary Retrieves or modifies the configuration options for this snoowrap instance. * @param {object} [options] A map of `{[config property name]: value}`. Note that any omitted config properties will simply - retain whatever value they had previously. (In other words, if you only want to change one property, you only need to put - that one property in this parameter. To get the current configuration without modifying anything, simply omit this - parameter.) + * retain whatever value they had previously. (In other words, if you only want to change one property, you only need to put + * that one property in this parameter. To get the current configuration without modifying anything, simply omit this + * parameter.) * @param {string} [options.endpointDomain='reddit.com'] The endpoint where requests should be sent * @param {Number} [options.requestDelay=0] A minimum delay, in milliseconds, to enforce between API calls. If multiple - api calls are requested during this timespan, they will be queued and sent one at a time. Setting this to more than 1000 will - ensure that reddit's ratelimit is never reached, but it will make things run slower than necessary if only a few requests - are being sent. If this is set to zero, snoowrap will not enforce any delay between individual requests. However, it will - still refuse to continue if reddit's enforced ratelimit (600 requests per 10 minutes) is exceeded. + * api calls are requested during this timespan, they will be queued and sent one at a time. Setting this to more than 1000 will + * ensure that reddit's ratelimit is never reached, but it will make things run slower than necessary if only a few requests + * are being sent. If this is set to zero, snoowrap will not enforce any delay between individual requests. However, it will + * still refuse to continue if reddit's enforced ratelimit (600 requests per 10 minutes) is exceeded. * @param {Number} [options.requestTimeout=30000] A timeout for all OAuth requests, in milliseconds. If the reddit server - fails to return a response within this amount of time, the Promise will be rejected with a timeout error. + * fails to return a response within this amount of time, the Promise will be rejected with a timeout error. * @param {boolean} [options.continueAfterRatelimitError=false] Determines whether snoowrap should queue API calls if - reddit's ratelimit is exceeded. If set to `true` when the ratelimit is exceeded, snoowrap will queue all further requests, - and will attempt to send them again after the current ratelimit period expires (which happens every 10 minutes). If set - to `false`, snoowrap will simply throw an error when reddit's ratelimit is exceeded. + * reddit's ratelimit is exceeded. If set to `true` when the ratelimit is exceeded, snoowrap will queue all further requests, + * and will attempt to send them again after the current ratelimit period expires (which happens every 10 minutes). If set + * to `false`, snoowrap will simply throw an error when reddit's ratelimit is exceeded. * @param {Number[]} [options.retryErrorCodes=[502, 503, 504, 522]] If reddit responds to an idempotent request with one of - these error codes, snoowrap will retry the request, up to a maximum of `max_retry_attempts` requests in total. (These - errors usually indicate that there was an temporary issue on reddit's end, and retrying the request has a decent chance of - success.) This behavior can be disabled by simply setting this property to an empty array. + * these error codes, snoowrap will retry the request, up to a maximum of `max_retry_attempts` requests in total. (These + * errors usually indicate that there was an temporary issue on reddit's end, and retrying the request has a decent chance of + * success.) This behavior can be disabled by simply setting this property to an empty array. * @param {Number} [options.maxRetryAttempts=3] See `retryErrorCodes`. * @param {boolean} [options.warnings=true] snoowrap may occasionally log warnings, such as deprecation notices, to the - console. These can be disabled by setting this to `false`. + * console. These can be disabled by setting this to `false`. * @param {boolean} [options.debug=false] If set to true, snoowrap will print out potentially-useful information for debugging - purposes as it runs. + * purposes as it runs. * @param {object} [options.logger=console] By default, snoowrap will log any warnings and debug output to the console. - A custom logger object may be supplied via this option; it must expose `warn`, `info`, `debug`, and `trace` functions. + * A custom logger object may be supplied via this option; it must expose `warn`, `info`, `debug`, and `trace` functions. * @param {boolean} [options.proxies=true] Setting this to `false` disables snoowrap's method-chaining feature. This causes - the syntax for using snoowrap to become a bit heavier, but allows for consistency between environments that support the ES6 - `Proxy` object and environments that don't. This option is a no-op in environments that don't support the `Proxy` object, - since method chaining is always disabled in those environments. Note, changing this setting must be done before making - any requests. + * the syntax for using snoowrap to become a bit heavier, but allows for consistency between environments that support the ES6 + * `Proxy` object and environments that don't. This option is a no-op in environments that don't support the `Proxy` object, + * since method chaining is always disabled in those environments. Note, changing this setting must be done before making + * any requests. * @returns {object} An updated Object containing all of the configuration values * @example * @@ -394,8 +407,8 @@ const snoowrap = class snoowrap { } } - get _promiseWrap () { - return this._config.proxies ? promiseWrap : identity; + _newObject (objectType, content, _hasFetched = false) { + return Array.isArray(content) ? content : new snoowrap.objects[objectType](content, this, _hasFetched); } /** @@ -416,16 +429,24 @@ const snoowrap = class snoowrap { /** * @summary Gets information on a comment with a given id. * @param {string} commentId - The base36 id of the comment + * @param {string|null} [submissionId] - The id of the submission that the comment belongs to. The replies + * tree will only be available when providing this param. However you still can fetch it separately + * @param {string} [sort] - Determines how the replies tree should be sorted. One of `confidence, + * top, new, controversial, old, random, qa, live` * @returns {Comment} An unfetched Comment object for the requested comment * @example * - * r.getComment('c0b6xx0') - * // => Comment { name: 't1_c0b6xx0' } - * r.getComment('c0b6xx0').author.name.then(console.log) + * const comment = r.getComment('c0b6xx0', '92dd8', 'new') + * // => Comment { name: 't1_c0b6xx0', link_id: 't3_92dd8', _sort: 'new' } + * comment.fetch().then(cmt => console.log(cmt.author.name)) * // => 'Kharos' */ - getComment (commentId) { - return this._newObject('Comment', {name: addFullnamePrefix(commentId, 't1_')}); + getComment (commentId, submissionId, sort) { + return this._newObject('Comment', { + name: addFullnamePrefix(commentId, 't1_'), + link_id: submissionId ? addFullnamePrefix(submissionId, 't3_') : null, + _sort: sort + }); } /** @@ -446,16 +467,18 @@ const snoowrap = class snoowrap { /** * @summary Gets information on a given submission. * @param {string} submissionId - The base36 id of the submission + * @param {string} [sort] - Determines how the comments tree should be sorted. One of `confidence, + * top, new, controversial, old, random, qa, live` * @returns {Submission} An unfetched Submission object for the requested submission * @example * - * r.getSubmission('2np694') - * // => Submission { name: 't3_2np694' } - * r.getSubmission('2np694').title.then(console.log) + * const submission = r.getSubmission('2np694', 'top') + * // => Submission { name: 't3_2np694', _sort: 'top' } + * submission.fetch().then(sub => console.log(sub.title)) * // => 'What tasty food would be distusting if eaten over rice?' */ - getSubmission (submissionId) { - return this._newObject('Submission', {name: addFullnamePrefix(submissionId, 't3_')}); + getSubmission (submissionId, sort) { + return this._newObject('Submission', {name: addFullnamePrefix(submissionId, 't3_'), _sort: sort}); } /** @@ -497,15 +520,14 @@ const snoowrap = class snoowrap { * r.getMe().then(console.log); * // => RedditUser { is_employee: false, has_mail: false, name: 'snoowrap_testing', ... } */ - getMe () { - return this._get({uri: 'api/v1/me'}).then(result => { - this._ownUserInfo = this._newObject('RedditUser', result, true); - return this._ownUserInfo; - }); + async getMe () { + const result = await this._get({url: 'api/v1/me'}); + this._ownUserInfo = this._newObject('RedditUser', result, true); + return this._ownUserInfo; } - _getMyName () { - return Promise.resolve(this._ownUserInfo ? this._ownUserInfo.name : this.getMe().get('name')); + async _getMyName () { + return this._ownUserInfo ? this._ownUserInfo.name : (await this.getMe()).name; } /** @@ -521,7 +543,7 @@ const snoowrap = class snoowrap { * // ] */ getKarma () { - return this._get({uri: 'api/v1/me/karma'}); + return this._get({url: 'api/v1/me/karma'}); } /** @@ -533,7 +555,7 @@ const snoowrap = class snoowrap { * // => { default_theme_sr: null, threaded_messages: true, hide_downs: false, ... } */ getPreferences () { - return this._get({uri: 'api/v1/me/prefs'}); + return this._get({url: 'api/v1/me/prefs'}); } /** @@ -544,11 +566,11 @@ const snoowrap = class snoowrap { * @example * * r.updatePreferences({threaded_messages: false, hide_downs: true}) - * // => { default_theme_sr: null, threaded_messages: false,hide_downs: true, ... } + * // => { default_theme_sr: null, threaded_messages: false, hide_downs: true, ... } * // (preferences updated on reddit) */ updatePreferences (updatedPreferences) { - return this._patch({uri: 'api/v1/me/prefs', body: updatedPreferences}); + return this._patch({url: 'api/v1/me/prefs', data: updatedPreferences}); } /** @@ -569,7 +591,7 @@ const snoowrap = class snoowrap { * // ] } */ getMyTrophies () { - return this._get({uri: 'api/v1/me/trophies'}); + return this._get({url: 'api/v1/me/trophies'}); } /** @@ -581,7 +603,7 @@ const snoowrap = class snoowrap { * // => [ [ RedditUser { date: 1457927963, name: 'not_an_aardvark', id: 't2_k83md' } ], [] ] */ getFriends () { - return this._get({uri: 'prefs/friends'}); + return this._get({url: 'prefs/friends'}); } /** @@ -593,7 +615,7 @@ const snoowrap = class snoowrap { * // => [ RedditUser { date: 1457928120, name: 'actually_an_aardvark', id: 't2_q3519' } ] */ getBlockedUsers () { - return this._get({uri: 'prefs/blocked'}); + return this._get({url: 'prefs/blocked'}); } /** @@ -605,7 +627,7 @@ const snoowrap = class snoowrap { * // => false */ checkCaptchaRequirement () { - return this._get({uri: 'api/needs_captcha'}); + return this._get({url: 'api/needs_captcha'}); } /** @@ -616,8 +638,9 @@ const snoowrap = class snoowrap { * r.getNewCaptchaIdentifier().then(console.log) * // => 'o5M18uy4mk0IW4hs0fu2GNPdXb1Dxe9d' */ - getNewCaptchaIdentifier () { - return this._post({uri: 'api/new_captcha', form: {api_type}}).then(res => res.json.data.iden); + async getNewCaptchaIdentifier () { + const res = await this._post({url: 'api/new_captcha', form: {api_type}}); + return res.json.data.iden; } /** @@ -630,7 +653,7 @@ const snoowrap = class snoowrap { // => (A long, incoherent string representing the image in PNG format) */ getCaptchaImage (identifier) { - return this._get({uri: `captcha/${identifier}`}); + return this._get({url: `captcha/${identifier}`}); } /** @@ -641,8 +664,9 @@ const snoowrap = class snoowrap { * r.getSavedCategories().then(console.log) * // => [ { category: 'cute cat pictures' }, { category: 'interesting articles' } ] */ - getSavedCategories () { - return this._get({uri: 'api/saved_categories'}).get('categories'); + async getSavedCategories () { + const res = await this._get({url: 'api/saved_categories'}); + return res.categories; } /** @@ -657,84 +681,540 @@ const snoowrap = class snoowrap { * // (the links will now appear purple on reddit) */ markAsVisited (links) { - return this._post({uri: 'api/store_visits', links: map(links, 'name').join(',')}); + return this._post({url: 'api/store_visits', form: {links: links.map(sub => sub.name).join(',')}}); } - _submit ({ - captcha_response, captchaResponse = captcha_response, - captcha_iden, captchaIden = captcha_iden, + async _submit ({ + subreddit_name, subredditName = subreddit_name, kind, - resubmit = true, - send_replies = true, sendReplies = send_replies, - crosspost_fullname, - text, title, url, - subreddit_name, subredditName = subreddit_name, - nsfw, - spoiler, + videoPosterUrl, + websocketUrl, + gallery, + text, + rtjson, + choices, + duration, + crosspost_fullname, crosspostFullname = crosspost_fullname, + resubmit = true, + send_replies = true, sendReplies = send_replies, + nsfw = false, + spoiler = false, flairId, flairText, + collectionId, + discussionType, + captcha_response, captchaResponse = captcha_response, + captcha_iden, captchaIden = captcha_iden, ...options }) { - return this._post({ - uri: 'api/submit', form: { - api_type, captcha: captchaResponse, iden: captchaIden, sendreplies: sendReplies, sr: subredditName, kind, resubmit, - crosspost_fullname, text, title, url, spoiler, nsfw, flair_id: flairId, flair_text: flairText, ...options + let ws; + if (websocketUrl) { + ws = new WebSocket(websocketUrl); + await new Promise((resolve, reject) => { + ws.onopen = resolve; + ws.onerror = () => reject(new errors.WebSocketError('Websocket error.')); + }); + ws.onerror = null; + } + + /** + * Todo: still unsure if `options.resubmit` is supported on gallery/poll submissions + */ + let result; + switch (kind) { + case 'gallery': + result = await this._post({ + url: 'api/submit_gallery_post.json', data: { + api_type, sr: subredditName, title, items: gallery, resubmit, sendreplies: sendReplies, nsfw, spoiler, + flair_id: flairId, flair_text: flairText, collection_id: collectionId, discussion_type: discussionType, + captcha: captchaResponse, iden: captchaIden, ...options + } + }); + break; + case 'poll': + result = await this._post({ + url: 'api/submit_poll_post', data: { + api_type, sr: subredditName, title, text, options: choices, duration, resubmit, sendreplies: sendReplies, nsfw, + spoiler, flair_id: flairId, flair_text: flairText, collection_id: collectionId, discussion_type: discussionType, + captcha: captchaResponse, iden: captchaIden, ...options + } + }); + break; + default: + result = await this._post({ + url: 'api/submit', form: { + api_type, sr: subredditName, kind, title, url, video_poster_url: videoPosterUrl, text, richtext_json: JSON.stringify(rtjson), + crosspost_fullname: crosspostFullname, resubmit, sendreplies: sendReplies, nsfw, spoiler, flair_id: flairId, flair_text: flairText, + collection_id: collectionId, discussion_type: discussionType, captcha: captchaResponse, iden: captchaIden, ...options + } + }); + break; + } + handleJsonErrors(result); + + if (ws) { + if (ws.readyState !== WebSocket.OPEN) { + throw new errors.WebSocketError('Websocket error. Your post may still have been created.'); + } + return new Promise((resolve, reject) => { + ws.onmessage = event => { + ws.onclose = null; + ws.close(); + const data = JSON.parse(event.data); + if (data.type === 'failed') { + reject(new errors.MediaPostFailedError()); + } + const submissionUrl = data.payload.redirect; + const submissionId = SUBMISSION_ID_REGEX.exec(submissionUrl)[1]; + resolve(this.getSubmission(submissionId)); + }; + ws.onerror = () => reject(new errors.WebSocketError('Websocket error. Your post may still have been created.')); + ws.onclose = () => reject(new errors.WebSocketError('Websocket closed. Your post may still have been created.')); + }); + } + return result.json.data.id ? this.getSubmission(result.json.data.id) : null; + } + + /** + * @summary Creates a new link submission on the given subreddit. + * @param {object} options An object containing details about the submission. + * @param {string} options.subredditName The name of the subreddit that the post should be submitted to. + * @param {string} options.title The title of the submission. + * @param {string} options.url The url that the link submission should point to. + * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission. + * @param {boolean} [options.resubmit=true] If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + * @param {boolean} [options.spoiler=false] Whether or not the submission should be marked as a spoiler. + * @param {boolean} [options.nsfw=false] Whether or not the submission should be marked NSFW. + * @param {string} [options.flairId] The flair template to select. + * @param {string} [options.flairText] If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + * @param {string} [options.collectionId] The UUID of a collection to add the newly-submitted post to. + * @param {string} [options.discussionType] Set to `CHAT` to enable live discussion instead of traditional comments. + * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier. + * @returns {Promise} The newly-created Submission object. + * @example + * + * r.submitLink({ + * subredditName: 'snoowrap_testing', + * title: 'I found a cool website!', + * url: 'https://google.com' + * }).then(console.log) + * // => Submission { name: 't3_4abnfe' } + * // (new linkpost created on reddit) + */ + submitLink (options) { + // Todo: Add `options.url` validation. + return this._submit({...options, kind: 'link'}); + } + + /** + * @summary Submit an image submission to the given subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param {object} options An object containing details about the submission. + * @param {string} options.subredditName The name of the subreddit that the post should be submitted to. + * @param {string} options.title The title of the submission. + * @param {string|stream.Readable|Blob|File|MediaImg} options.imageFile The image that should get submitted. This should either be the path to + * the image file you want to upload, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) / + * [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) / [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments + * (e.g. browsers) where the filesystem is unavailable. Alternatively you can diractly pass a ready-to-use {@link MediaImg} instead. + * See {@link snoowrap#uploadMedia} for more details. + * @param {string} options.imageFileName The name that the image file should have. Required when it cannot be diractly extracted from + * the provided file (e.g ReadableStream, Blob). + * @param {boolean} [options.noWebsockets=false] Set to `true` to disable use of WebSockets. If `true`, this method will return `null`. + * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission. + * @param {boolean} [options.resubmit=true] If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + * @param {boolean} [options.spoiler=false] Whether or not the submission should be marked as a spoiler. + * @param {boolean} [options.nsfw=false] Whether or not the submission should be marked NSFW. + * @param {string} [options.flairId] The flair template to select. + * @param {string} [options.flairText] If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + * @param {string} [options.collectionId] The UUID of a collection to add the newly-submitted post to. + * @param {string} [options.discussionType] Set to `CHAT` to enable live discussion instead of traditional comments. + * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier. + * @returns {Promise} The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const blob = await (await fetch("https://example.com/kittens.jpg")).blob() + * r.submitImage({ + * subredditName: 'snoowrap_testing', + * title: 'Take a look at those cute kittens <3', + * imageFile: blob, // Usage as a `Blob`. + * imageFileName: 'kittens.jpg' + * }).then(console.log) + * // => Submission + * // (new image submission created on reddit) + */ + async submitImage ({ + imageFile, + imageFileName, + noWebsockets, + ...options + }) { + let url, websocketUrl; + try { + const {fileUrl, websocketUrl: wsUrl} = imageFile instanceof MediaImg + ? imageFile + : await this.uploadMedia({ + file: imageFile, + name: imageFileName, + type: 'img' + }); + url = fileUrl; + websocketUrl = wsUrl; + } catch (err) { + throw new Error('An error has occurred with the image file: ' + err.message); + } + return this._submit({...options, kind: 'image', url, websocketUrl: noWebsockets ? null : websocketUrl}); + } + + /** + * @summary Submit a video or videogif submission to the given subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param {object} options An object containing details about the submission. + * @param {string} options.subredditName The name of the subreddit that the post should be submitted to. + * @param {string} options.title The title of the submission. + * @param {string|stream.Readable|Blob|File|MediaVideo} options.videoFile The video that should get submitted. This should either be the path to + * the video file you want to upload, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) / + * [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) / [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments + * (e.g. browsers) where the filesystem is unavailable. Alternatively you can diractly pass a ready-to-use {@link MediaVideo} instead. + * See {@link snoowrap#uploadMedia} for more details. + * @param {string} options.videoFileName The name that the video file should have. Required when it cannot be diractly extracted from + * the provided file (e.g ReadableStream, Blob). + * @param {string|stream.Readable|Blob|File|MediaImg} options.thumbnailFile The image that should get uploaded and used as a thumbnail for the video. This + * should either be the path to the image file you want to upload, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) / + * [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) / [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments + * (e.g. browsers) where the filesystem is unavailable. Alternatively you can diractly pass a ready-to-use {@link MediaImg} instead. + * See {@link snoowrap#uploadMedia} for more details. + * @param {string} options.thumbnailFileName The name that the thumbnail file should have. Required when it cannot be diractly extracted from + * the provided file (e.g ReadableStream, Blob). + * @param {boolean} [options.videogif=false] If `true`, the video is submitted as a videogif, which is essentially a silent video. + * @param {boolean} [options.noWebsockets=false] Set to `true` to disable use of WebSockets. If `true`, this method will return `null`. + * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission. + * @param {boolean} [options.resubmit=true] If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + * @param {boolean} [options.spoiler=false] Whether or not the submission should be marked as a spoiler. + * @param {boolean} [options.nsfw=false] Whether or not the submission should be marked NSFW. + * @param {string} [options.flairId] The flair template to select. + * @param {string} [options.flairText] If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + * @param {string} [options.collectionId] The UUID of a collection to add the newly-submitted post to. + * @param {string} [options.discussionType] Set to `CHAT` to enable live discussion instead of traditional comments. + * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier. + * @returns {Promise} The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const mediaVideo = await r.uploadMedia({ + * file: './video.mp4', + * type: 'video' + * }) + * r.submitVideo({ + * subredditName: 'snoowrap_testing', + * title: 'This is a video!', + * videoFile: mediaVideo, // Usage as a `MediaVideo`. + * thumbnailFile: fs.createReadStream('./thumbnail.png'), // Usage as a `stream.Readable`. + * thumbnailFileName: 'thumbnail.png' + * }).then(console.log) + * // => Submission + * // (new video submission created on reddit) + */ + async submitVideo ({ + videoFile, + videoFileName, + thumbnailFile, + thumbnailFileName, + videogif = false, + noWebsockets, + ...options + }) { + let url, videoPosterUrl, websocketUrl; + const kind = videogif ? 'videogif' : 'video'; + + /** + * Imagin you just finished uploading a large video, then oops! you faced this error: "An error has occurred with the thumbnail file"! + * In this case we should validate the thumbnail parameters first to ensure that no accidental uploads will happen. + */ + if (!(thumbnailFile instanceof MediaImg)) { + try { + await this.uploadMedia({ + file: thumbnailFile, + name: thumbnailFileName, + type: 'img', + validateOnly: true + }); + } catch (err) { + throw new Error('An error has occurred with the thumbnail file: ' + err.message); + } + } + + /** + * Now we are safe to upload. If the provided video is invalid the error can be easly catched. + */ + try { + const {fileUrl, websocketUrl: wsUrl} = videoFile instanceof MediaVideo + ? videoFile + : await this.uploadMedia({ + file: videoFile, + name: videoFileName, + type: videogif ? 'gif' : 'video' + }); + url = fileUrl; + websocketUrl = wsUrl; + } catch (err) { + throw new Error('An error has occurred with the video file: ' + err.message); + } + try { + const {fileUrl} = + thumbnailFile instanceof MediaImg + ? thumbnailFile + : await this.uploadMedia({ + file: thumbnailFile, + name: thumbnailFileName, + type: 'img' + }); + videoPosterUrl = fileUrl; + } catch (err) { + throw new Error('An error occurred with the thumbnail file: ' + err.message); + } + + return this._submit({...options, kind, url, videoPosterUrl, websocketUrl: noWebsockets ? null : websocketUrl}); + } + + /** + * @summary Submit a gallery to the given subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param {object} options An object containing details about the submission. + * @param {string} options.subredditName The name of the subreddit that the post should be submitted to. + * @param {string} options.title The title of the submission. + * @param {Array} options.gallery An array containing 2 to 20 gallery items. Currently only images are accepted. A gallery item should + * either be a {@link MediaImg}, or an object containing `imageFile` and `imageFileName` (the same as `options.imageFile` and `options.imageFileName` + * used in {@link snoowrap#submitImage}) in addition of an optional `caption` with a maximum of 180 characters along with an optional `outboundUrl` + * (the same as {@link MediaImg#caption} and {@link MediaImg#outboundUrl}). + * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission. + * @param {boolean} [options.resubmit=true] If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + * @param {boolean} [options.spoiler=false] Whether or not the submission should be marked as a spoiler. + * @param {boolean} [options.nsfw=false] Whether or not the submission should be marked NSFW. + * @param {string} [options.flairId] The flair template to select. + * @param {string} [options.flairText] If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + * @param {string} [options.collectionId] The UUID of a collection to add the newly-submitted post to. + * @param {string} [options.discussionType] Set to `CHAT` to enable live discussion instead of traditional comments. + * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier. + * @returns {Promise} The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const fileinput = document.getElementById('file-input') + * const files = fileinput.files.map(file => { // Usage as an array of `File`s. + * return { + * imageFile: file, + * caption: file.name + * } + * }) + * const blob = await (await fetch("https://example.com/kittens.jpg")).blob() + * const mediaImg = await r.uploadMedia({ // Usage as a `MediaImg`. + * file: blob, + * type: 'img', + * caption: 'cute :3', + * outboundUrl: 'https://example.com/kittens.html' + * }) + * r.submitGallery({ + * subredditName: 'snoowrap_testing', + * title: 'This is a gallery!', + * gallery: [mediaImg, ...files] + * }).then(console.log) + * // => Submission + * // (new gallery submission created on reddit) + */ + async submitGallery ({gallery, ...options}) { + /** + * Validate every single gallery item to ensure that no accidental uploads will happen. + */ + await Promise.all(gallery.map(async (item, index) => { + try { + if (item.caption.length > 180) { + throw new Error('Caption must be 180 characters or less.'); + } + // Todo: Add outboundUrl validation. + if (!(item instanceof MediaImg)) { + await this.uploadMedia({ + file: item.imageFile, + name: item.imageFileName, + type: 'img', + validateOnly: true + }); + } + } catch (err) { + throw new Error(`An error has occurred with a gallery item at the index ${index}: ` + err.message); + } + })); + + /** + * Now we are safe to upload. It still depends on network conditions tho, that's why it is recommended to pass the gallery items + * as ready-to-use `MediaImg`s instead. + */ + gallery = await Promise.all(gallery.map(async (item, index) => { + try { + if (!(item instanceof MediaImg)) { + item = await this.uploadMedia({ + file: item.imageFile, + name: item.imageFileName, + type: 'img', + caption: item.caption, + outboundUrl: item.outboundUrl + }); + } + } catch (err) { + throw new Error(`An error occurred with a gallery item at the index ${index}: ` + err.message); } - }).tap(handleJsonErrors(this)).then(result => this.getSubmission(result.json.data.id)); + return { + caption: item.caption, + outbound_url: item.outboundUrl, + media_id: item.assetId + }; + })); + + return this._submit({...options, kind: 'gallery', gallery}); } /** * @summary Creates a new selfpost on the given subreddit. - * @param {object} options An object containing details about the submission - * @param {string} options.subredditName The name of the subreddit that the post should be submitted to - * @param {string} options.title The title of the submission - * @param {string} [options.text] The selftext of the submission - * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission + * @param {object} options An object containing details about the submission. + * @param {string} options.subredditName The name of the subreddit that the post should be submitted to. + * @param {string} options.title The title of the submission. + * @param {string} [options.text] The selftext of the submission. + * @param {object} [options.inlineMedia] An object containing inctances of `MediaFile` subclasses, or `options` to pass to + * {@link snoowrap#uploadMedia} where `options.type` is required. The keys of this object can be used as placeholders in + * `options.text` with the format `{key}`. + * @param {string} [options.rtjson] The body of the submission in `richtext_json` format. See {@link snoowrap#convertToFancypants} + * for more details. This will override `options.text` and `options.inlineMedia`. + * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission. + * @param {boolean} [options.resubmit=true] If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + * @param {boolean} [options.spoiler=false] Whether or not the submission should be marked as a spoiler. + * @param {boolean} [options.nsfw=false] Whether or not the submission should be marked NSFW. + * @param {string} [options.flairId] The flair template to select. + * @param {string} [options.flairText] If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + * @param {string} [options.collectionId] The UUID of a collection to add the newly-submitted post to. + * @param {string} [options.discussionType] Set to `CHAT` to enable live discussion instead of traditional comments. * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account - requires a captcha to submit posts and comments. - * @param {string} [options.captchaResponse] The response to the captcha with the given identifier - * @returns {Promise} The newly-created Submission object + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier. + * @returns {Promise} The newly-created Submission object. * @example * + * const mediaVideo = await r.uploadMedia({ + * file: './video.mp4', + * type: 'video', + * caption: 'Short video!' + * }) * r.submitSelfpost({ * subredditName: 'snoowrap_testing', * title: 'This is a selfpost', - * text: 'This is the text body of the selfpost' + * text: 'This is the text body of the selfpost.\n\nAnd This is an inline image {img} And also a video! {vid}', + * inlineMedia: { + * img: { + * file: './animated.gif', // Usage as a file path. + * type: 'img' + * }, + * vid: mediaVideo + * } * }).then(console.log) - * // => Submission { name: 't3_4abmsz' } + * // => Submission * // (new selfpost created on reddit) */ - submitSelfpost (options) { - return this._submit({...options, kind: 'self'}); - } + async submitSelfpost ({text, inlineMedia, rtjson, ...options}) { + /* eslint-disable require-atomic-updates */ + if (rtjson) { + text = null; + } + if (text && inlineMedia) { + const placeholders = Object.keys(inlineMedia); - /** - * @summary Creates a new link submission on the given subreddit. - * @param {object} options An object containing details about the submission - * @param {string} options.subredditName The name of the subreddit that the post should be submitted to - * @param {string} options.title The title of the submission - * @param {string} options.url The url that the link submission should point to - * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission - * @param {boolean} [options.resubmit=true] If this is false and same link has already been submitted to this subreddit in - the past, reddit will return an error. This could be used to avoid accidental reposts. + // Validate inline media + await Promise.all(placeholders.map(async p => { + if (!text.includes(`{${p}}`)) { + return; + } + if (!(inlineMedia[p] instanceof MediaFile)) { + await this.uploadMedia({ + ...inlineMedia[p], + validateOnly: true + }); + } + })); + + // Upload if necessary + await Promise.all(placeholders.map(async p => { + if (!text.includes(`{${p}}`)) { + return; + } + if (!(inlineMedia[p] instanceof MediaFile)) { + inlineMedia[p] = await this.uploadMedia({ + ...inlineMedia[p] + }); + } + })); + + const body = text.replace(PLACEHOLDER_REGEX, (_m, g1) => inlineMedia[g1]); + rtjson = await this.convertToFancypants(body); + text = null; + } + return this._submit({...options, kind: 'self', text, rtjson}); + /* eslint-enable require-atomic-updates */ + } + + /** + * @summary Submit a poll to the given subreddit. (Undocumented endpoint). + * @param {object} options An object containing details about the submission. + * @param {string} options.subredditName The name of the subreddit that the post should be submitted to. + * @param {string} options.title The title of the submission. + * @param {string} [options.text] The selftext of the submission. + * @param {string[]} options.choices An array of 2 to 6 poll options. + * @param {number} options.duration The number of days the poll should accept votes. Valid values are between 1 and 7, inclusive. + * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission. + * @param {boolean} [options.resubmit=true] If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + * @param {boolean} [options.spoiler=false] Whether or not the submission should be marked as a spoiler. + * @param {boolean} [options.nsfw=false] Whether or not the submission should be marked NSFW. + * @param {string} [options.flairId] The flair template to select. + * @param {string} [options.flairText] If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + * @param {string} [options.collectionId] The UUID of a collection to add the newly-submitted post to. + * @param {string} [options.discussionType] Set to `CHAT` to enable live discussion instead of traditional comments. * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account - requires a captcha to submit posts and comments. - * @param {string} [options.captchaResponse] The response to the captcha with the given identifier - * @returns {Promise} The newly-created Submission object + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier. + * @returns {Promise} The newly-created Submission object. * @example * - * r.submitLink({ + * r.submitPoll({ * subredditName: 'snoowrap_testing', - * title: 'I found a cool website!', - * url: 'https://google.com' + * title: 'Survey!', + * text: 'Do you like snoowrap?', + * choices: ['YES!', 'NOPE!'], + * duration: 3 * }).then(console.log) - * // => Submission { name: 't3_4abnfe' } - * // (new linkpost created on reddit) + * // => Submission + * // (new poll submission created on reddit) */ - submitLink (options) { - return this._submit({...options, kind: 'link'}); + submitPoll (options) { + return this._submit({...options, kind: 'poll'}); } /** @@ -745,32 +1225,201 @@ const snoowrap = class snoowrap { * @param {string} options.subredditName The name of the subreddit that the crosspost should be submitted to * @param {string} options.title The title of the crosspost * @param {(string|Submission)} options.originalPost A Submission object or a post ID for the original post which - is being crossposted - * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission - * @param {boolean} [options.resubmit=true] If this is false and same link has already been submitted to this subreddit in - the past, reddit will return an error. This could be used to avoid accidental reposts. + * is being crossposted + * @param {boolean} [options.sendReplies=true] Determines whether inbox replies should be enabled for this submission. + * @param {boolean} [options.resubmit=true] If this is `false` and same link has already been submitted to this subreddit in the past, + * reddit will return an error. This could be used to avoid accidental reposts. + * @param {boolean} [options.spoiler=false] Whether or not the submission should be marked as a spoiler. + * @param {boolean} [options.nsfw=false] Whether or not the submission should be marked NSFW. + * @param {string} [options.flairId] The flair template to select. + * @param {string} [options.flairText] If a flair template is selected and its property `flair_text_editable` is `true`, this will + * customize the flair text. + * @param {string} [options.collectionId] The UUID of a collection to add the newly-submitted post to. + * @param {string} [options.discussionType] Set to `CHAT` to enable live discussion instead of traditional comments. + * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier. * @returns {Promise} The newly-created Submission object * @example * - * await r.submitCrosspost({ title: 'I found an interesting post', originalPost: '6vths0', subredditName: 'snoowrap' }) + * r.submitCrosspost({ + * title: 'I found an interesting post', + * originalPost: '6vths0', + * subredditName: 'snoowrap' + * }).then(console.log) + * // => Submission + * // (new crosspost submission created on reddit) */ - submitCrosspost (options) { + submitCrosspost ({originalPost, ...options}) { return this._submit({ ...options, kind: 'crosspost', - crosspost_fullname: options.originalPost instanceof snoowrap.objects.Submission - ? options.originalPost.name - : addFullnamePrefix(options.originalPost, 't3_') + crosspostFullname: originalPost instanceof snoowrap.objects.Submission + ? originalPost.name + : addFullnamePrefix(originalPost, 't3_') }); } + /** + * @summary Upload media to reddit (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param {object} options An object contains the media file to upload. + * @param {string|stream.Readable|Blob|File} options.file The media file that should get uploaded. This should either be the path to the file + * you want to upload, or a [ReadableStream](https://nodejs.org/api/stream.html#stream_class_stream_readable) / + * [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) / [File](https://developer.mozilla.org/en-US/docs/Web/API/File) in environments + * (e.g. browsers) where the filesystem is unavailable. + * @param {string} options.name The name that the file should have. Required when it cannot be diractly extracted from the provided + * file (e.g ReadableStream, Blob). + * @param {string} [options.type] Determines the media file type. This should be one of `img, video, gif`. + * @param {boolean} [options.validateOnly] If true, the file won't get uploaded, and this method will return `null`. Useful if you only want + * to validate the parameters before actually uploading the file. + * @returns {Promise} A Promise that fulfills with an instance of {@link MediaImg} / {@link MediaVideo} / {@link MediaGif} / {@link MediaFile} + * depending on the value of `options.type`. Or `null` when `options.validateOnly` is set to `true`. + * @example + * + * const blob = await (await fetch("https://example.com/video.mp4")).blob() + * r.uploadMedia({ + * file: blob, + * name: 'video.mp4', + * type: 'gif', + * caption: 'This is a silent video!' + * }).then(console.log) + * // => MediaGif + * + * r.uploadMedia({ + * file: './meme.jpg', + * caption: 'Funny!', + * outboundUrl: 'https://example.com' + * }).then(console.log) + * // => MediaFile + */ + async uploadMedia ({file, name, type, caption, outboundUrl, validateOnly = false}) { + if (isBrowser && typeof fetch === 'undefined') { + throw new errors.InvalidMethodCallError('Your browser doesn\'t support \'no-cors\' requests'); + } + if (isBrowser && typeof file === 'string') { + throw new errors.InvalidMethodCallError('Uploaded file cannot be a string on browser'); + } + // `File` is a specific kind of `Blob`, so one check for `Blob` is enough + if (typeof file !== 'string' && !(file instanceof stream.Readable) && !(typeof Blob !== 'undefined' && file instanceof Blob)) { + throw new errors.InvalidMethodCallError('Uploaded file must either be a string, a ReadableStream, a Blob or a File'); + } + const parsedFile = typeof file === 'string' ? createReadStream(file) : file; + const fileName = typeof file === 'string' ? path.basename(file) : file.name || name; + if (!fileName) { + requiredArg('name'); + } + let fileExt = path.extname(fileName) || 'jpeg'; // Default to JPEG + fileExt = fileExt.replace('.', ''); + const mimetype = typeof Blob !== 'undefined' && file instanceof Blob && file.type ? file.type : MIME_TYPES[fileExt] || ''; + const expectedMimePrefix = MEDIA_TYPES[type]; + if (expectedMimePrefix && mimetype.split('/')[0] !== expectedMimePrefix) { + throw new errors.InvalidMethodCallError(`Expected a mimetype for the file '${fileName}' starting with '${expectedMimePrefix}' but got '${mimetype}'`); + } + // Todo: The file size should be checked + if (validateOnly) { + return null; + } + const uploadResponse = await this._post({ + url: 'api/media/asset.json', + form: { + filepath: fileName, + mimetype + } + }); + const uploadURL = 'https:' + uploadResponse.args.action; + const fileInfo = { + fileUrl: uploadURL + '/' + uploadResponse.args.fields.find(item => item.name === 'key').value, + assetId: uploadResponse.asset.asset_id, + websocketUrl: uploadResponse.asset.websocket_url, + caption, + outboundUrl + }; + const formdata = new FormData(); + uploadResponse.args.fields.forEach(item => formdata.append(item.name, item.value)); + formdata.append('file', parsedFile, fileName); + let res; + if (isBrowser) { + res = await fetch(uploadURL, { + method: 'post', + mode: 'no-cors', + body: formdata + }); + this._debug('Response:', res); + /** + * Todo: Since the response of 'no-cors' requests cannot contain the status code, the uploaded file should be validated + * by setting `fileInfo.fileUrl` as the `src` attribute of an img/video element and listening to the load event. + */ + } else { + const contentLength = await new Promise((resolve, reject) => { + formdata.getLength((err, length) => { + if (err) { + reject(err); + } + resolve(length); + }); + }); + res = await this.rawRequest({ + url: uploadURL, + method: 'post', + headers: { + 'user-agent': this.userAgent, + 'content-type': `multipart/form-data; boundary=${formdata._boundary}`, + 'content-length': contentLength + }, + data: formdata, + _r: this + }); + } + let media; + switch (type) { + case 'img': + media = new MediaImg(fileInfo); + break; + case 'video': + media = new MediaVideo(fileInfo); + break; + case 'gif': + media = new MediaGif(fileInfo); + break; + default: + media = new MediaFile(fileInfo); + break; + } + return media; + } + + /** + * @summary Convert `markdown` to `richtext_json` format that used on the fancy pants editor. This format allows + * to embed inline media on selfposts. + * @param {string} markdown The Markdown text to convert. + * @returns {Promise} A Promise that fulfills with an object in `richtext_json` format. + * @example + * + * r.convertToFancypants('Hello **world**!').then(console.log) + * // => object {document: Array(1)} + */ + async convertToFancypants (markdown) { + const response = await this._post({ + uri: 'api/convert_rte_body_format', + form: { + output_mode: 'rtjson', + markdown_text: markdown + } + }); + return response.output; + } + _getSortedFrontpage (sortType, subredditName, options = {}) { // Handle things properly if only a time parameter is provided but not the subreddit name let opts = options; let subName = subredditName; if (typeof subredditName === 'object' && isEmpty(omitBy(opts, option => option === undefined))) { - /* In this case, "subredditName" ends up referring to the second argument, which is not actually a name since the user - decided to omit that parameter. */ + /** + * In this case, "subredditName" ends up referring to the second argument, which is not actually a name since the user + * decided to omit that parameter. + */ opts = subredditName; subName = undefined; } @@ -781,7 +1430,7 @@ const snoowrap = class snoowrap { /** * @summary Gets a Listing of hot posts. * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from - the front page of reddit. + * the front page of reddit. * @param {object} [options={}] Options for the resulting Listing * @returns {Promise} A Listing containing the retrieved submissions * @example @@ -802,7 +1451,7 @@ const snoowrap = class snoowrap { * * r.getHot('redditdev', {limit: 1}).then(console.log) * // => Listing [ - // Submission { domain: 'self.redditdev', banned_by: null, subreddit: Subreddit { display_name: 'redditdev' }, ...} + * // Submission { domain: 'self.redditdev', banned_by: null, subreddit: Subreddit { display_name: 'redditdev' }, ...} * // ] */ getHot (subredditName, options) { @@ -824,7 +1473,7 @@ const snoowrap = class snoowrap { * * r.getBest({limit: 1}).then(console.log) * // => Listing [ - // Submission { domain: 'self.redditdev', banned_by: null, subreddit: Subreddit { display_name: 'redditdev' }, ...} + * // Submission { domain: 'self.redditdev', banned_by: null, subreddit: Subreddit { display_name: 'redditdev' }, ...} * // ] */ getBest (options) { @@ -834,7 +1483,7 @@ const snoowrap = class snoowrap { /** * @summary Gets a Listing of new posts. * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from - the front page of reddit. + * the front page of reddit. * @param {object} [options={}] Options for the resulting Listing * @returns {Promise} A Listing containing the retrieved submissions * @example @@ -854,7 +1503,7 @@ const snoowrap = class snoowrap { /** * @summary Gets a Listing of new comments. * @param {string} [subredditName] The subreddit to get comments from. If not provided, posts are fetched from - the front page of reddit. + * the front page of reddit. * @param {object} [options={}] Options for the resulting Listing * @returns {Promise} A Listing containing the retrieved comments * @example @@ -872,10 +1521,11 @@ const snoowrap = class snoowrap { /** * @summary Get list of content by IDs. Returns a listing of the requested content. * @param {Array} ids An array of content IDs. Can include the id itself, or a Submission or Comment object. -can get a post and a comment * @returns {Promise>} A listing of content requested, can be any class fetchable by API. e.g. Comment, Submission + * can get a post and a comment + * @returns {Promise>} A listing of content requested, can be any class fetchable by API. e.g. Comment, Submission * @example * - * r.getContentByIds(['t3_9l9vof','t3_9la341']).then(console.log); + * r.getContentByIds(['t3_9l9vof', 't3_9la341']).then(console.log); * // => Listing [ * // Submission { approved_at_utc: null, ... } * // Submission { approved_at_utc: null, ... } @@ -904,32 +1554,35 @@ can get a post and a comment * @returns {Promise> throw new TypeError('Id must be either a string, Submission, or Comment.'); }); - return this._get({uri: '/api/info', method: 'get', qs: {id: prefixedIds.join(',')}}); + return this._get({url: '/api/info', params: {id: prefixedIds.join(',')}}); } /** * @summary Gets a single random Submission. - * @desc **Note**: This function will not work when snoowrap is running in a browser, because the reddit server sends a - redirect which cannot be followed by a CORS request. + * @desc **Notes**: This function will not work when snoowrap is running in a browser, because the reddit server sends a + * redirect which cannot be followed by a CORS request. Also, due to a known API issue, this function won't work with subreddits + * excluded from /r/all, since the reddit server returns the subreddit itself instead of a random submission, in this case + * the function will return `null`. * @param {string} [subredditName] The subreddit to get the random submission. If not provided, the post is fetched from - the front page of reddit. - * @returns {Promise} The retrieved Submission object + * the front page of reddit. + * @returns {Promise|null} The retrieved Submission object when available * @example * * r.getRandomSubmission('aww').then(console.log) * // => Submission { domain: 'i.imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'aww' }, ... } */ - getRandomSubmission (subredditName) { - return this._get({uri: `${subredditName ? `r/${subredditName}/` : ''}random`}); + async getRandomSubmission (subredditName) { + const res = await this._get({url: `${subredditName ? `r/${subredditName}/` : ''}random`}); + return res instanceof snoowrap.objects.Submission ? res : null; } /** * @summary Gets a Listing of top posts. * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from - the front page of reddit. + * the front page of reddit. * @param {object} [options={}] Options for the resulting Listing * @param {string} [options.time] Describes the timespan that posts should be retrieved from. Should be one of - `hour, day, week, month, year, all` + * `hour, day, week, month, year, all` * @returns {Promise} A Listing containing the retrieved submissions * @example * @@ -954,10 +1607,10 @@ can get a post and a comment * @returns {Promise> /** * @summary Gets a Listing of controversial posts. * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from - the front page of reddit. + * the front page of reddit. * @param {object} [options={}] Options for the resulting Listing * @param {string} [options.time] Describes the timespan that posts should be retrieved from. Should be one of - `hour, day, week, month, year, all` + * `hour, day, week, month, year, all` * @returns {Promise} A Listing containing the retrieved submissions * @example * @@ -974,7 +1627,7 @@ can get a post and a comment * @returns {Promise> /** * @summary Gets a Listing of controversial posts. * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from - the front page of reddit. + * the front page of reddit. * @param {object} [options] Options for the resulting Listing * @returns {Promise} A Listing containing the retrieved submissions * @example @@ -1009,8 +1662,8 @@ can get a post and a comment * @returns {Promise> * @summary Gets the items in the authenticated user's inbox. * @param {object} [options={}] Filter options. Can also contain options for the resulting Listing. * @param {string} [options.filter] A filter for the inbox items. If provided, it should be one of `unread`, (unread - items), `messages` (i.e. PMs), `comments` (comment replies), `selfreply` (selfpost replies), or `mentions` (username - mentions). + * items), `messages` (i.e. PMs), `comments` (comment replies), `selfreply` (selfpost replies), or `mentions` (username + * mentions). * @returns {Promise} A Listing containing items in the user's inbox * @example * @@ -1094,18 +1747,19 @@ can get a post and a comment * @returns {Promise> * }).then(console.log) * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... } */ - createModmailDiscussion ({ + async createModmailDiscussion ({ body, subject, srName }) { const parsedFromSr = srName.replace(/^\/?r\//, ''); // Convert '/r/subreddit_name' to 'subreddit_name' - // _newObject ignores most of the response, no practical way to parse the returned content yet - return this._post({ - uri: 'api/mod/conversations', form: { + const res = await this._post({ + url: 'api/mod/conversations', form: { body, subject, srName: parsedFromSr } - }).then(res => this._newObject('ModmailConversation', {id: res.conversation.id})); + }); + // _newObject ignores most of the response, no practical way to parse the returned content yet + return this._newObject('ModmailConversation', {id: res.conversation.id}); } /** @@ -1130,7 +1784,7 @@ can get a post and a comment * @returns {Promise> */ markNewModmailConversationsAsRead (conversations) { const conversationIds = conversations.map(message => addFullnamePrefix(message, '')); - return this._post({uri: 'api/mod/conversations/read', form: {conversationIds: conversationIds.join(',')}}); + return this._post({url: 'api/mod/conversations/read', form: {conversationIds: conversationIds.join(',')}}); } /** @@ -1142,7 +1796,7 @@ can get a post and a comment * @returns {Promise> */ markNewModmailConversationsAsUnread (conversations) { const conversationIds = conversations.map(message => addFullnamePrefix(message, '')); - return this._post({uri: 'api/mod/conversations/unread', form: {conversationIds: conversationIds.join(',')}}); + return this._post({url: 'api/mod/conversations/unread', form: {conversationIds: conversationIds.join(',')}}); } /** @@ -1156,12 +1810,9 @@ can get a post and a comment * @returns {Promise> * // Subreddit { display_name: 'EarthPorn', ... }, * // ] */ - getNewModmailSubreddits () { - return this._get({uri: 'api/mod/conversations/subreddits'}).then(response => { - return Object.values(response.subreddits).map(s => { - return this._newObject('Subreddit', s); - }); - }); + async getNewModmailSubreddits () { + const response = await this._get({url: 'api/mod/conversations/subreddits'}); + return Object.values(response.subreddits).map(s => this._newObject('Subreddit', s)); } /** @@ -1194,7 +1845,7 @@ can get a post and a comment * @returns {Promise> * // } */ getUnreadNewModmailConversationsCount () { - return this._get({uri: 'api/mod/conversations/unread/count'}); + return this._get({url: 'api/mod/conversations/unread/count'}); } /** @@ -1216,19 +1867,16 @@ can get a post and a comment * @returns {Promise> * // ModmailConversation { id: '75hxg' } * // ] */ - bulkReadNewModmail (subreddits, state) { + async bulkReadNewModmail (subreddits, state) { const subredditNames = subreddits.map(s => typeof s === 'string' ? s.replace(/^\/?r\//, '') : s.display_name); - return this._post({uri: 'api/mod/conversations/bulk/read', form: { + const res = await this._post({url: 'api/mod/conversations/bulk/read', form: { entity: subredditNames.join(','), state - }}).then(res => { - return this._newObject('Listing', { - after: null, - before: null, - children: res.conversation_ids.map(id => { - return this._newObject('ModmailConversation', {id}); - }) - }); + }}); + return this._newObject('Listing', { + after: null, + before: null, + children: res.conversation_ids.map(id => this._newObject('ModmailConversation', {id})) }); } @@ -1251,8 +1899,8 @@ can get a post and a comment * @returns {Promise> /** * @summary Marks all of the given messages as read. * @param {PrivateMessage[]|String[]} messages An Array of PrivateMessage or Comment objects. Can also contain strings - representing message or comment IDs. If strings are provided, they are assumed to represent PrivateMessages unless a fullname - prefix such as `t1_` is specified. + * representing message or comment IDs. If strings are provided, they are assumed to represent PrivateMessages unless a fullname + * prefix such as `t1_` is specified. * @returns {Promise} A Promise that fulfills when the request is complete * @example * @@ -1267,14 +1915,14 @@ can get a post and a comment * @returns {Promise> */ markMessagesAsRead (messages) { const messageIds = messages.map(message => addFullnamePrefix(message, 't4_')); - return this._post({uri: 'api/read_message', form: {id: messageIds.join(',')}}); + return this._post({url: 'api/read_message', form: {id: messageIds.join(',')}}); } /** * @summary Marks all of the given messages as unread. * @param {PrivateMessage[]|String[]} messages An Array of PrivateMessage or Comment objects. Can also contain strings - representing message IDs. If strings are provided, they are assumed to represent PrivateMessages unless a fullname prefix such - as `t1_` is included. + * representing message IDs. If strings are provided, they are assumed to represent PrivateMessages unless a fullname prefix such + * as `t1_` is included. * @returns {Promise} A Promise that fulfills when the request is complete * @example * @@ -1289,13 +1937,13 @@ can get a post and a comment * @returns {Promise> */ markMessagesAsUnread (messages) { const messageIds = messages.map(message => addFullnamePrefix(message, 't4_')); - return this._post({uri: 'api/unread_message', form: {id: messageIds.join(',')}}); + return this._post({url: 'api/unread_message', form: {id: messageIds.join(',')}}); } /** * @summary Marks all of the user's messages as read. * @desc **Note:** The reddit.com site imposes a ratelimit of approximately 1 request every 10 minutes on this endpoint. - Further requests will cause the API to return a 429 error. + * Further requests will cause the API to return a 429 error. * @returns {Promise} A Promise that resolves when the request is complete * @example * @@ -1306,7 +1954,7 @@ can get a post and a comment * @returns {Promise> * // (messages marked as 'read' on reddit) */ readAllMessages () { - return this._post({uri: 'api/read_all_messages'}); + return this._post({url: 'api/read_all_messages'}); } /** @@ -1316,9 +1964,9 @@ can get a post and a comment * @returns {Promise> * @param {string} options.subject The message subject (100 characters max) * @param {string} options.text The body of the message, in raw markdown text * @param {Subreddit|string} [options.fromSubreddit] If provided, the message is sent as a modmail from the specified - subreddit. + * subreddit. * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account - requires a captcha to submit posts and comments. + * requires a captcha to submit posts and comments. * @param {string} [options.captchaResponse] The response to the captcha with the given identifier * @returns {Promise} A Promise that fulfills when the request is complete * @example @@ -1330,7 +1978,7 @@ can get a post and a comment * @returns {Promise> * }) * // (message created on reddit) */ - composeMessage ({ + async composeMessage ({ captcha, from_subreddit, fromSubreddit = from_subreddit, captcha_iden, captchaIden = captcha_iden, @@ -1350,11 +1998,13 @@ can get a post and a comment * @returns {Promise> } else if (typeof fromSubreddit === 'string') { parsedFromSr = fromSubreddit.replace(/^\/?r\//, ''); // Convert '/r/subreddit_name' to 'subreddit_name' } - return this._post({ - uri: 'api/compose', form: { + const result = await this._post({ + url: 'api/compose', form: { api_type, captcha, iden: captchaIden, from_sr: parsedFromSr, subject, text, to: parsedTo } - }).tap(handleJsonErrors(this)).return({}); + }); + handleJsonErrors(result); + return {}; } /** @@ -1379,7 +2029,7 @@ can get a post and a comment * @returns {Promise> * // } */ getOauthScopeList () { - return this._get({uri: 'api/v1/scopes'}); + return this._get({url: 'api/v1/scopes'}); } /** @@ -1387,7 +2037,7 @@ can get a post and a comment * @returns {Promise> * @param {object} options Search options. Can also contain options for the resulting Listing. * @param {string} options.query The search query * @param {string} [options.time] Describes the timespan that posts should be retrieved from. One of - `hour, day, week, month, year, all` + * `hour, day, week, month, year, all` * @param {Subreddit|string} [options.subreddit] The subreddit to conduct the search on. * @param {boolean} [options.restrictSr=true] Restricts search results to the given subreddit * @param {string} [options.sort] Determines how the results should be sorted. One of `relevance, hot, top, new, comments` @@ -1435,11 +2085,12 @@ can get a post and a comment * @returns {Promise> * // ... * // ] */ - searchSubredditNames ({exact = false, include_nsfw = true, includeNsfw = include_nsfw, query}) { - return this._post({uri: 'api/search_reddit_names', qs: {exact, include_over_18: includeNsfw, query}}).get('names'); + async searchSubredditNames ({exact = false, include_nsfw = true, includeNsfw = include_nsfw, query}) { + const res = await this._post({url: 'api/search_reddit_names', params: {exact, include_over_18: includeNsfw, query}}); + return res.names; } - _createOrEditSubreddit ({ + async _createOrEditSubreddit ({ allow_images = true, allow_top = true, captcha, @@ -1474,8 +2125,8 @@ can get a post and a comment * @returns {Promise> wikimode = 'modonly', ...otherKeys }) { - return this._post({ - uri: 'api/site_admin', form: { + const res = await this._post({ + url: 'api/site_admin', form: { allow_images, allow_top, api_type, captcha, collapse_deleted_comments, comment_score_hide_mins, description, exclude_banned_modqueue, 'header-title': header_title, hide_ads, iden: captcha_iden, lang, link_type, name, over_18, public_description, public_traffic, show_media, show_media_preview, spam_comments, spam_links, @@ -1483,7 +2134,9 @@ can get a post and a comment * @returns {Promise> title, type, wiki_edit_age, wiki_edit_karma, wikimode, ...otherKeys } - }).then(handleJsonErrors(this.getSubreddit(name || sr))); + }); + handleJsonErrors(res); + return this.getSubreddit(name || sr); } /** @@ -1492,49 +2145,49 @@ can get a post and a comment * @returns {Promise> * @param {string} options.name The name of the new subreddit * @param {string} options.title The text that should appear in the header of the subreddit * @param {string} options.public_description The text that appears with this subreddit on the search page, or on the - blocked-access page if this subreddit is private. (500 characters max) + * blocked-access page if this subreddit is private. (500 characters max) * @param {string} options.description The sidebar text for the subreddit. (5120 characters max) * @param {string} [options.submit_text=''] The text to show below the submission page (1024 characters max) * @param {boolean} [options.hide_ads=false] Determines whether ads should be hidden on this subreddit. (This is only - allowed for gold-only subreddits.) + * allowed for gold-only subreddits.) * @param {string} [options.lang='en'] The language of the subreddit (represented as an IETF language tag) * @param {string} [options.type='public'] Determines who should be able to access the subreddit. This should be one of - `public, private, restricted, gold_restricted, gold_only, archived, employees_only`. + * `public, private, restricted, gold_restricted, gold_only, archived, employees_only`. * @param {string} [options.link_type='any'] Determines what types of submissions are allowed on the subreddit. This should - be one of `any, link, self`. + * be one of `any, link, self`. * @param {string} [options.submit_link_label=undefined] Custom text to display on the button that submits a link. If - this is omitted, the default text will be displayed. + * this is omitted, the default text will be displayed. * @param {string} [options.submit_text_label=undefined] Custom text to display on the button that submits a selfpost. If - this is omitted, the default text will be displayed. + * this is omitted, the default text will be displayed. * @param {string} [options.wikimode='modonly'] Determines who can edit wiki pages on the subreddit. This should be one of - `modonly, anyone, disabled`. + * `modonly, anyone, disabled`. * @param {number} [options.wiki_edit_karma=0] The minimum amount of subreddit karma needed for someone to edit this - subreddit's wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) + * subreddit's wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) * @param {number} [options.wiki_edit_age=0] The minimum account age (in days) needed for someone to edit this subreddit's - wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) + * wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) * @param {string} [options.spam_links='high'] The spam filter strength for links on this subreddit. This should be one of - `low, high, all`. + * `low, high, all`. * @param {string} [options.spam_selfposts='high'] The spam filter strength for selfposts on this subreddit. This should be - one of `low, high, all`. + * one of `low, high, all`. * @param {string} [options.spam_comments='high'] The spam filter strength for comments on this subreddit. This should be one - of `low, high, all`. + * of `low, high, all`. * @param {boolean} [options.over_18=false] Determines whether this subreddit should be classified as NSFW * @param {boolean} [options.allow_top=true] Determines whether the new subreddit should be able to appear in /r/all and - trending subreddits + * trending subreddits * @param {boolean} [options.show_media=false] Determines whether image thumbnails should be enabled on this subreddit * @param {boolean} [options.show_media_preview=true] Determines whether media previews should be expanded by default on this - subreddit + * subreddit * @param {boolean} [options.allow_images=true] Determines whether image uploads and links to image hosting sites should be - enabled on this subreddit + * enabled on this subreddit * @param {boolean} [options.exclude_banned_modqueue=false] Determines whether posts by site-wide banned users should be - excluded from the modqueue. + * excluded from the modqueue. * @param {boolean} [options.public_traffic=false] Determines whether the /about/traffic page for this subreddit should be - viewable by anyone. + * viewable by anyone. * @param {boolean} [options.collapse_deleted_comments=false] Determines whether deleted and removed comments should be - collapsed by default + * collapsed by default * @param {string} [options.suggested_comment_sort=undefined] The suggested comment sort for the subreddit. This should be - one of `confidence, top, new, controversial, old, random, qa`.If left blank, there will be no suggested sort, - which means that users will see the sort method that is set in their own preferences (usually `confidence`.) + * one of `confidence, top, new, controversial, old, random, qa`.If left blank, there will be no suggested sort, + * which means that users will see the sort method that is set in their own preferences (usually `confidence`.) * @param {boolean} [options.spoilers_enabled=false] Determines whether users can mark their posts as spoilers * @returns {Promise} A Promise for the newly-created subreddit object. * @example @@ -1569,8 +2222,9 @@ can get a post and a comment * @returns {Promise> * // ... * // ] */ - searchSubredditTopics ({query}) { - return this._get({uri: 'api/subreddits_by_topic', qs: {query}}).map(result => this.getSubreddit(result.name)); + async searchSubredditTopics ({query}) { + const results = await this._get({url: 'api/subreddits_by_topic', params: {query}}); + return results.map(result => this.getSubreddit(result.name)); } /** @@ -1706,7 +2360,7 @@ can get a post and a comment * @returns {Promise> /** * @summary Checks whether a given username is available for registration * @desc **Note:** This function will not work when snoowrap is running in a browser, due to an issue with reddit's CORS - settings. + * settings. * @param {string} name The username in question * @returns {Promise} A Promise that fulfills with a Boolean (`true` or `false`) * @example @@ -1718,7 +2372,7 @@ can get a post and a comment * @returns {Promise> */ checkUsernameAvailability (name) { // The oauth endpoint listed in reddit's documentation doesn't actually work, so just send an unauthenticated request. - return this.unauthenticatedRequest({uri: 'api/username_available.json', qs: {user: name}}); + return this.unauthenticatedRequest({url: 'api/username_available.json', params: {user: name}}); } /** @@ -1734,22 +2388,24 @@ can get a post and a comment * @returns {Promise> * r.createLivethread({title: 'My livethread'}).then(console.log) * // => LiveThread { id: 'wpimncm1f01j' } */ - createLivethread ({title, description, resources, nsfw = false}) { - return this._post({ - uri: 'api/live/create', + async createLivethread ({title, description, resources, nsfw = false}) { + const result = await this._post({ + url: 'api/live/create', form: {api_type, description, nsfw, resources, title} - }).tap(handleJsonErrors(this)).then(result => this.getLivethread(result.json.data.id)); + }); + handleJsonErrors(result); + return this.getLivethread(result.json.data.id); } /** * @summary Gets the "happening now" LiveThread, if it exists * @desc This is the LiveThread that is occasionally linked at the top of reddit.com, relating to current events. * @returns {Promise} A Promise that fulfills with the "happening now" LiveThread if it exists, or rejects with a 404 error - otherwise. + * otherwise. * @example r.getCurrentEventsLivethread().then(thread => thread.stream.on('update', console.log)) */ getStickiedLivethread () { - return this._get({uri: 'api/live/happening_now'}); + return this._get({url: 'api/live/happening_now'}); } /** @@ -1761,7 +2417,7 @@ can get a post and a comment * @returns {Promise> * => [ MultiReddit { ... }, MultiReddit { ... }, ... ] */ getMyMultireddits () { - return this._get({uri: 'api/multi/mine', qs: {expand_srs: true}}); + return this._get({url: 'api/multi/mine', params: {expand_srs: true}}); } /** @@ -1772,9 +2428,9 @@ can get a post and a comment * @returns {Promise> * @param {Array} options.subreddits An Array of Subreddit objects (or subreddit names) that this multireddit should compose of * @param {string} [options.visibility='private'] The multireddit's visibility setting. One of `private`, `public`, `hidden`. * @param {string} [options.icon_name=''] One of `art and design`, `ask`, `books`, `business`, `cars`, `comics`, - `cute animals`, `diy`, `entertainment`, `food and drink`, `funny`, `games`, `grooming`, `health`, `life advice`, `military`, - `models pinup`, `music`, `news`, `philosophy`, `pictures and gifs`, `science`, `shopping`, `sports`, `style`, `tech`, - `travel`, `unusual stories`, `video`, `None` + * `cute animals`, `diy`, `entertainment`, `food and drink`, `funny`, `games`, `grooming`, `health`, `life advice`, `military`, + * `models pinup`, `music`, `news`, `philosophy`, `pictures and gifs`, `science`, `shopping`, `sports`, `style`, `tech`, + * `travel`, `unusual stories`, `video`, `None` * @param {string} [options.key_color='#000000'] A six-digit RGB hex color, preceded by '#' * @param {string} [options.weighting_scheme='classic'] One of `classic`, `fresh` * @returns {Promise} A Promise for the newly-created MultiReddit object @@ -1792,7 +2448,7 @@ can get a post and a comment * @returns {Promise> weighting_scheme = 'classic' }) { return this._post({ - uri: 'api/multi', form: { + url: 'api/multi', form: { model: JSON.stringify({ display_name: name, description_md: description, @@ -1807,61 +2463,64 @@ can get a post and a comment * @returns {Promise> } _revokeToken (token) { - return this.credentialedClientRequest({uri: 'api/v1/revoke_token', form: {token}, method: 'post'}); + return this.credentialedClientRequest({url: 'api/v1/revoke_token', form: {token}, method: 'post'}); } /** * @summary Invalidates the current access token. * @returns {Promise} A Promise that fulfills when this request is complete * @desc **Note**: This can only be used if the current requester was supplied with a `client_id` and `client_secret`. If the - current requester was supplied with a refresh token, it will automatically create a new access token if any more requests - are made after this one. + * current requester was supplied with a refresh token, it will automatically create a new access token if any more requests + * are made after this one. * @example r.revokeAccessToken(); */ - revokeAccessToken () { - return this._revokeToken(this.accessToken).then(() => { - this.accessToken = null; - this.tokenExpiration = null; - }); + async revokeAccessToken () { + await this._revokeToken(this.accessToken); + this.accessToken = null; + this.tokenExpiration = null; + this.scope = null; } /** * @summary Invalidates the current refresh token. * @returns {Promise} A Promise that fulfills when this request is complete * @desc **Note**: This can only be used if the current requester was supplied with a `client_id` and `client_secret`. All - access tokens generated by this refresh token will also be invalidated. This effectively de-authenticates the requester and - prevents it from making any more valid requests. This should only be used in a few cases, e.g. if this token has - been accidentally leaked to a third party. + * access tokens generated by this refresh token will also be invalidated. This effectively de-authenticates the requester and + * prevents it from making any more valid requests. This should only be used in a few cases, e.g. if this token has + * been accidentally leaked to a third party. * @example r.revokeRefreshToken(); */ - revokeRefreshToken () { - return this._revokeToken(this.refreshToken).then(() => { - this.refreshToken = null; - this.accessToken = null; // Revoking a refresh token also revokes any associated access tokens. - this.tokenExpiration = null; - }); + async revokeRefreshToken () { + await this._revokeToken(this.refreshToken); + this.refreshToken = null; + this.accessToken = null; // Revoking a refresh token also revokes any associated access tokens. + this.tokenExpiration = null; + this.scope = null; } - _selectFlair ({flair_template_id, link, name, text, subredditName}) { + async _selectFlair ({flair_template_id, link, name, text, subredditName}) { if (!flair_template_id) { throw new errors.InvalidMethodCallError('No flair template ID provided'); } - return Promise.resolve(subredditName).then(subName => { - return this._post({uri: `r/${subName}/api/selectflair`, form: {api_type, flair_template_id, link, name, text}}); - }); + return this._post({url: `r/${subredditName}/api/selectflair`, form: {api_type, flair_template_id, link, name, text}}); } - _assignFlair ({css_class, cssClass = css_class, link, name, text, subreddit_name, subredditName = subreddit_name}) { - return this._promiseWrap(Promise.resolve(subredditName).then(displayName => { - return this._post({uri: `r/${displayName}/api/flair`, form: {api_type, name, text, link, css_class: cssClass}}); - })); + async _assignFlair ({css_class, cssClass = css_class, link, name, text, subreddit_name, subredditName = subreddit_name}) { + return this._post({url: `r/${subredditName}/api/flair`, form: {api_type, name, text, link, css_class: cssClass}}); } - _populate (responseTree) { + _populate (responseTree, children = {}, nested) { if (typeof responseTree === 'object' && responseTree !== null) { // Map {kind: 't2', data: {name: 'some_username', ... }} to a RedditUser (e.g.) with the same properties if (Object.keys(responseTree).length === 2 && responseTree.kind && responseTree.data) { - return this._newObject(KINDS[responseTree.kind] || 'RedditContent', this._populate(responseTree.data), true); + const populated = this._newObject(KINDS[responseTree.kind] || 'RedditContent', this._populate(responseTree.data, children, true), true); + if (!nested && Object.keys(children).length) { + populated._children = children; + } + if (populated instanceof snoowrap.objects.Comment) { + children[populated.id] = populated; + } + return populated; } const result = (Array.isArray(responseTree) ? map : mapValues)(responseTree, (value, key) => { // Maps {author: 'some_username'} to {author: RedditUser { name: 'some_username' } } @@ -1871,7 +2530,7 @@ can get a post and a comment * @returns {Promise> if (value !== null && SUBREDDIT_KEYS.has(key)) { return this._newObject('Subreddit', {display_name: value}); } - return this._populate(value); + return this._populate(value, children, true); }); if (result.length === 2 && result[0] instanceof snoowrap.objects.Listing && result[0][0] instanceof snoowrap.objects.Submission && result[1] instanceof snoowrap.objects.Listing) { @@ -1879,29 +2538,37 @@ can get a post and a comment * @returns {Promise> result[1]._more.link_id = result[0][0].name; } result[0][0].comments = result[1]; + result[0][0]._children = children; return result[0][0]; } + if (!nested && Object.keys(children).length) { + result._children = children; + } return result; } return responseTree; } _getListing ({uri, qs = {}, ...options}) { - /* When the response type is expected to be a Listing, add a `count` parameter with a very high number. - This ensures that reddit returns a `before` property in the resulting Listing to enable pagination. - (Aside from the additional parameter, this function is equivalent to snoowrap.prototype._get) */ + /** + * When the response type is expected to be a Listing, add a `count` parameter with a very high number. + * This ensures that reddit returns a `before` property in the resulting Listing to enable pagination. + * (Aside from the additional parameter, this function is equivalent to snoowrap.prototype._get) + */ const mergedQuery = {count: 9999, ...qs}; return qs.limit || !isEmpty(options) ? this._newObject('Listing', {_query: mergedQuery, _uri: uri, ...options}).fetchMore(qs.limit || MAX_LISTING_ITEMS) - /* This second case is used as a fallback in case the endpoint unexpectedly ends up returning something other than a - Listing (e.g. Submission#getRelated, which used to return a Listing but no longer does due to upstream reddit API - changes), in which case using fetch_more() as above will throw an error. - - This fallback only works if there are no other meta-properties provided for the Listing, such as _transform. If there are - other meta-properties, the function will still end up throwing an error, but there's not really any good way to handle it - (predicting upstream changes can only go so far). More importantly, in the limited cases where it's used, the fallback - should have no effect on the returned results */ - : this._get({uri, qs: mergedQuery}).then(listing => { + /** + * This second case is used as a fallback in case the endpoint unexpectedly ends up returning something other than a + * Listing (e.g. Submission#getRelated, which used to return a Listing but no longer does due to upstream reddit API + * changes), in which case using fetch_more() as above will throw an error. + * + * This fallback only works if there are no other meta-properties provided for the Listing, such as _transform. If there are + * other meta-properties, the function will still end up throwing an error, but there's not really any good way to handle it + * (predicting upstream changes can only go so far). More importantly, in the limited cases where it's used, the fallback + * should have no effect on the returned results + */ + : this._get({url: uri, params: mergedQuery}).then(listing => { if (Array.isArray(listing)) { listing.filter(item => item.constructor._name === 'Comment').forEach(addEmptyRepliesListing); } @@ -1911,7 +2578,7 @@ can get a post and a comment * @returns {Promise> /** * @summary In browsers, restores the `window.snoowrap` property to whatever it was before this instance of snoowrap was - loaded. This is a no-op in Node. + * loaded. This is a no-op in Node. * @returns This instance of the snoowrap constructor * @example var snoowrap = window.snoowrap.noConflict(); */ @@ -1938,24 +2605,30 @@ defineInspectFunc(snoowrap.prototype, function () { const classFuncDescriptors = {configurable: true, writable: true}; -/* Add the request_handler functions (oauth_request, credentialed_client_request, etc.) to the snoowrap prototype. Use -Object.defineProperties to ensure that the properties are non-enumerable. */ +/** + * Add the request_handler functions (oauth_request, credentialed_client_request, etc.) to the snoowrap prototype. Use + * Object.defineProperties to ensure that the properties are non-enumerable. + */ Object.defineProperties(snoowrap.prototype, mapValues(requestHandler, func => ({value: func, ...classFuncDescriptors}))); HTTP_VERBS.forEach(method => { - /* Define method shortcuts for each of the HTTP verbs. i.e. `snoowrap.prototype._post` is the same as `oauth_request` except - that the HTTP method defaults to `post`, and the result is promise-wrapped. Use Object.defineProperty to ensure that the - properties are non-enumerable. */ + /** + * Define method shortcuts for each of the HTTP verbs. i.e. `snoowrap.prototype._post` is the same as `oauth_request` except + * that the HTTP method defaults to `post`, and the result is promise-wrapped. Use Object.defineProperty to ensure that the + * properties are non-enumerable. + */ Object.defineProperty(snoowrap.prototype, `_${method}`, { value (options) { - return this._promiseWrap(this.oauthRequest({...options, method})); + return this.oauthRequest({...options, method}); }, ...classFuncDescriptors }); }); -/* `objects` will be an object containing getters for each content type, due to the way objects are exported from -objects/index.js. To unwrap these getters into direct properties, use lodash.mapValues with an identity function. */ -snoowrap.objects = mapValues(objects, value => value); +/** + * `objects` will be an object containing getters for each content type, due to the way objects are exported from + * objects/index.js. To unwrap these getters into direct properties, use lodash.mapValues with an identity function. + */ +snoowrap.objects = mapValues(objects, identity); forOwn(KINDS, value => { snoowrap.objects[value] = snoowrap.objects[value] || class extends objects.RedditContent { diff --git a/src/snoowrap-tests.ts b/src/snoowrap-tests.ts.txt similarity index 99% rename from src/snoowrap-tests.ts rename to src/snoowrap-tests.ts.txt index 32d25ca2..1a96726f 100644 --- a/src/snoowrap-tests.ts +++ b/src/snoowrap-tests.ts.txt @@ -1,3 +1,4 @@ +// @ts-nocheck import Snoowrap = require('../dist/snoowrap'); import { Comment, diff --git a/src/snoowrap.ts b/src/snoowrap.ts new file mode 100644 index 00000000..cfd03b1e --- /dev/null +++ b/src/snoowrap.ts @@ -0,0 +1,2157 @@ +import stream from 'stream' +import fs from 'fs' +import {omit} from 'lodash' + +import BaseRequester from './BaseRequester' +import type {Common, AppAuth, ScriptAuth, CodeAuth, All} from './BaseRequester' + +import defaultObjects from './defaultObjects' +import * as objects from './objects' +import * as errors from './errors' +import { + KINDS, MAX_LISTING_ITEMS, MODULE_NAME, USER_KEYS, SUBREDDIT_KEYS, VERSION, MIME_TYPES, SUBMISSION_ID_REGEX, + MEDIA_TYPES, PLACEHOLDER_REGEX, type COMMENT_SORTS, type FRONTPAGE_SORTS, type SUBMISSION_SORTS +} from './constants' + +import {addEmptyRepliesListing, addFullnamePrefix, handleJsonErrors} from './helper' +import {isBrowser, path, requiredArg, FormData, WebSocket} from './helpers' +import isAxiosResponse, {type AxiosResponse} from './helpers/isAxiosResponse' +import isContentTree, {type ContentTree} from './helpers/isContentTree' +import isSubmissionTree, {type SubmissionTree} from './helpers/isSubmissionTree' + +import type { + Children, Fancypants, UploadResponse, SubredditOptions, UploadMediaOptions, + UploadInlineMediaOptions, MediaType, SubmitOptions, SubmitLinkOptions, SubmitImageOptions, SubmitVideoOptions, + SubmitGalleryOptions, SubmitSelfpostOptions, SubmitPollOptions, SubmitCrosspostOptions, AssignFlairOptions, SelectFlairOptions +} from './interfaces' + +import type {Listing, RedditUser, Submission, Subreddit, ModmailConversation, PrivateMessage} from './objects' +import type {ListingQuery, SortedListingQuery, Options} from './objects/Listing' +import MediaFile, {MediaImg, MediaVideo, MediaGif} from './objects/MediaFile' + +const fetch = self.fetch +const Blob = self.Blob +const api_type = 'json' + + +export interface ListingOptions extends Partial { + qs: Options['_query'] + uri: Options['_uri'] +} + +export interface InboxFilterQuery extends ListingQuery { + /** + * A filter for the inbox items. If provided, it should be one of `unread`, (unread + * items), `messages` (i.e. PMs), `comments` (comment replies), `selfreply` (selfpost replies), + * or `mentions` (username mentions). + */ + filter?: 'inbox'|'unread'|'messages'|'comments'|'selfreply'|'mentions' +} + +export interface SearchOptions extends SortedListingQuery { + /** The subreddit to conduct the search on. (Custom)*/ + subreddit?: Subreddit|string + /** The query string to search for. A string no longer than 512 characters. */ + q: string + /** Determines how the results should be sorted. */ + sort?: typeof SUBMISSION_SORTS[number]|'relevance'|'comments' + /** Specifies a syntax for the search. */ + syntax?: 'cloudsearch'|'lucene'|'plain' + /** Restricts search results to the given subreddit */ + restrict_sr?: boolean + /** A string no longer than 5 characters */ + category?: string + include_facets?: boolean + /** Expand subreddits */ + sr_detail?: string + /** Comma-delimited list of result types (sr, link, user) */ + type?: string +} + +type ObjectType = keyof typeof snoowrap['objects'] +type Objects = { + [Key in ObjectType]: InstanceType +} + +/** + * The class for a snoowrap requester. + * A requester is the base object that is used to fetch content from reddit. Each requester contains a single set of OAuth + * tokens. + * + * If constructed with a refresh token, a requester will be able to repeatedly generate access tokens as necessary, without any + * further user intervention. After making at least one request, a requester will have the `accessToken` property, which specifies + * the access token currently in use. It will also have a few additional properties such as `scope` (an array of scope strings) + * and `ratelimitRemaining` (the number of requests remaining for the current 10-minute interval, in compliance with reddit's + * [API rules](https://github.com/reddit/reddit/wiki/API).) These properties primarily exist for internal use, but they are + * exposed since they are useful externally as well. + */ +class snoowrap extends BaseRequester { + ['constructor']: typeof snoowrap + static _previousSnoowrap: typeof snoowrap + static errors = errors + static objects = {...defaultObjects, ...objects} + static version = VERSION + _ownUserInfo?: RedditUser + + /** + * @summary In browsers, restores the `window.snoowrap` property to whatever it was before this instance of snoowrap was + * loaded. This is a no-op in Node. + * @returns This instance of the snoowrap constructor + * @example var snoowrap = window.snoowrap.noConflict(); + */ + static noConflict () { + if (isBrowser) (self as any)[MODULE_NAME] = this._previousSnoowrap + return snoowrap + } + + _newObject (objectType: T, content: any[], _hasFetched?: boolean): any[] + _newObject (objectType: T, content: any, _hasFetched?: boolean): Objects[T] + _newObject (objectType: T, content: any[]|any, _hasFetched = false) { + if (Array.isArray(content)) return content + const object = snoowrap.objects[objectType] || snoowrap.objects.RedditContent + // @ts-ignore + return new object(content, this, _hasFetched) + } + + _populate (responseTree: any, children: Children = {}): any { + let nested = true, url: string + + if (isAxiosResponse(responseTree)) { + const axiosResponse: AxiosResponse = responseTree + url = `${axiosResponse.config.baseURL}/${axiosResponse.config.url}` + responseTree = axiosResponse.data + nested = false + } + + if (typeof responseTree !== 'object' || responseTree === null) { + return responseTree + } + + // Map {kind: 't2', data: {name: 'some_username', ... }} to a RedditUser (e.g.) with the same properties + if (isContentTree(responseTree)) { + const contentTree: ContentTree = responseTree + const populated = this._newObject( + KINDS[contentTree.kind] || 'RedditContent', + this._populate(contentTree.data, children), + true + ) + if (populated instanceof snoowrap.objects.Comment) { + children[populated.id] = populated + } + if (!nested && Object.keys(children).length) { + // @ts-ignore + populated._children = children + } + if (!nested && populated instanceof snoowrap.objects.Listing) { + populated._setUri(url!) + } + return populated + } + + for (const key of Object.keys(responseTree)) { + const value = responseTree[key] + // Maps {author: 'some_username'} to {author: RedditUser { name: 'some_username' } } + if (value !== null && USER_KEYS.has(key)) { + responseTree[key] = this._newObject('RedditUser', {name: value}) + } + if (value !== null && SUBREDDIT_KEYS.has(key)) { + responseTree[key] = this._newObject('Subreddit', {display_name: value}) + } + responseTree[key] = this._populate(value, children) + } + + if (isSubmissionTree(responseTree)) { + const submissionTree: SubmissionTree = responseTree + if (submissionTree[1]._more && !submissionTree[1]._more.link_id) { + submissionTree[1]._more.link_id = submissionTree[0][0].name + } + submissionTree[0][0].comments = submissionTree[1] + submissionTree[0][0]._children = children + return submissionTree[0][0] + } + + if (!nested && Object.keys(children).length) { + responseTree._children = children + } + + return responseTree + } + + async _assignFlair ({ + text, + css_class, + name, + link, + subredditName, + ...opts + }: AssignFlairOptions) { + if (!name && !link) { + throw new errors.InvalidMethodCallError('Either `name` or `link` should be provided') + } + return this._post({url: `r/${subredditName}/api/flair`, form: {...opts, api_type, text, css_class, link, name}}) + } + + async _selectFlair ({ + text, + flair_template_id, + name, + link, + subredditName, + ...opts + }: SelectFlairOptions) { + if (!flair_template_id) { + throw new errors.InvalidMethodCallError('No flair template ID provided') + } + if (!name && !link) { + throw new errors.InvalidMethodCallError('Either `name` or `link` should be provided') + } + return this._post({url: `r/${subredditName}/api/selectflair`, form: {...opts, api_type, text, flair_template_id, link, name}}) + } + + // #region _getListing + + async _getListing ({uri = '', qs = {}, ...opts}: ListingOptions) { + /** + * When the response type is expected to be a Listing, add a `count` parameter with a very high number. + * This ensures that reddit returns a `before` property in the resulting Listing to enable pagination. + * (Aside from the additional parameter, this function is equivalent to snoowrap.prototype._get) + */ + const query = {count: 9999, ...qs} + if (qs.limit || Object.keys(opts).length) { + const listing: Listing = this._newObject('Listing', {_query: query, _uri: uri, ...opts}) + return listing.fetchMore(qs.limit || MAX_LISTING_ITEMS) + } + /** + * This second case is used as a fallback in case the endpoint unexpectedly ends up returning something other than a + * Listing (e.g. Submission#getRelated, which used to return a Listing but no longer does due to upstream reddit API + * changes), in which case using fetchMore() as above will throw an error. + * + * This fallback only works if there are no other meta-properties provided for the Listing, such as _transform. If there are + * other meta-properties, the function will still end up throwing an error, but there's not really any good way to handle it + * (predicting upstream changes can only go so far). More importantly, in the limited cases where it's used, the fallback + * should have no effect on the returned results + */ + const listing: Listing = await this._get({url: uri, params: query}) + if (Array.isArray(listing)) { + listing.filter(item => item instanceof snoowrap.objects.Comment).forEach(addEmptyRepliesListing) + } + return listing + } + + /** + * @summary Gets a list of subreddits in which the currently-authenticated user is an approved submitter. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getContributorSubreddits().then(console.log) + * // => Listing [ + * // Subreddit { + * // display_name: 'snoowrap_testing', + * // title: 'snoowrap', + * // ... + * // } + * // ] + * + */ + getContributorSubreddits (options: ListingQuery): Promise> { + return this._getListing({uri: 'subreddits/mine/contributor', qs: options}) + } + + /** + * @summary Gets a list of default subreddits. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getDefaultSubreddits().then(console.log) + * // => Listing [ Subreddit { ... }, Subreddit { ... }, ...] + */ + getDefaultSubreddits (options: ListingQuery): Promise> { + return this._getListing({uri: 'subreddits/default', qs: options}) + } + + /** + * @summary Gets a list of gold-exclusive subreddits. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getGoldSubreddits().then(console.log) + * // => Listing [ Subreddit { ... }, Subreddit { ... }, ...] + */ + getGoldSubreddits (options: ListingQuery): Promise> { + return this._getListing({uri: 'subreddits/gold', qs: options}) + } + + /** + * @summary Gets the items in the authenticated user's inbox. + * @param {object} [options] Filter options. Can also contain options for the resulting Listing. + * @returns A Listing containing items in the user's inbox + * @example + * + * r.getInbox().then(console.log) + * // => Listing [ + * // PrivateMessage { body: 'hi!', was_comment: false, first_message: null, ... }, + * // Comment { body: 'this is a reply', link_title: 'Yay, a selfpost!', was_comment: true, ... } + * // ] + */ + getInbox ({filter, ...options}: InboxFilterQuery) { + return this._getListing({uri: `message/${filter || 'inbox'}`, qs: options}) + } + + /** + * @summary Gets a list of subreddits in which the currently-authenticated user is a moderator. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getModeratedSubreddits().then(console.log) + * // => Listing [ + * // Subreddit { + * // display_name: 'snoowrap_testing', + * // title: 'snoowrap', + * // ... + * // } + * // ] + */ + getModeratedSubreddits (options: ListingQuery): Promise> { + return this._getListing({uri: 'subreddits/mine/moderator', qs: options}) + } + + /** + * @summary Gets the authenticated user's modmail. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing of the user's modmail + * @example + * + * r.getModmail({limit: 2}).then(console.log) + * // => Listing [ + * // PrivateMessage { body: '/u/not_an_aardvark has accepted an invitation to become moderator ... ', ... }, + * // PrivateMessage { body: '/u/not_an_aardvark has been invited by /u/actually_an_aardvark to ...', ... } + * // ] + */ + getModmail (options: ListingQuery): Promise> { + return this._getListing({uri: 'message/moderator', qs: options}) + } + + /** + * @summary Gets a list of ModmailConversations from the authenticated user's subreddits. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getNewModmailConversations({limit: 2}).then(console.log) + * // => Listing [ + * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... }, + * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... } + * // ] + */ + getNewModmailConversations (options: ListingQuery): Promise> { + return this._getListing({ + uri: 'api/mod/conversations', + qs: options, + _transform: (response: any) => { + response.after = null + response.before = null + response.children = [] + + for (const conversation of response.conversationIds) { + response.conversations[conversation].participant = this._newObject('ModmailConversationAuthor', { + ...response.conversations[conversation].participant + }) + const conversationObjects = objects.ModmailConversation._getConversationObjects( + response.conversations[conversation], + response + ) + const data = { + ...conversationObjects, + ...response.conversations[conversation] + } + response.children.push(this._newObject('ModmailConversation', data)) + } + return this._newObject('Listing', response) + } + }) + } + + /** + * @summary Gets a list of subreddits, arranged by age. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getNewSubreddits().then(console.log) + * // => Listing [ Subreddit { ... }, Subreddit { ... }, ...] + */ + getNewSubreddits (options: ListingQuery): Promise> { + return this._getListing({uri: 'subreddits/new', qs: options}) + } + + /** + * @summary Gets a list of subreddits, arranged by popularity. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getPopularSubreddits().then(console.log) + * // => Listing [ Subreddit { ... }, Subreddit { ... }, ...] + */ + getPopularSubreddits (options: ListingQuery): Promise> { + return this._getListing({uri: 'subreddits/popular', qs: options}) + } + + /** + * @summary Gets the user's sent messages. + * @param {object} [options] options for the resulting Listing + * @returns A Listing of the user's sent messages + * @example + * + * r.getSentMessages().then(console.log) + * // => Listing [ + * // PrivateMessage { body: 'you have been added as an approved submitter to ...', ... }, + * // PrivateMessage { body: 'you have been banned from posting to ...' ... } + * // ] + */ + getSentMessages (options?: ListingQuery): Promise> { + return this._getListing({uri: 'message/sent', qs: options}) + } + + /** + * @summary Gets a list of subreddits that the currently-authenticated user is subscribed to. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing Subreddits + * @example + * + * r.getSubscriptions({limit: 2}).then(console.log) + * // => Listing [ + * // Subreddit { + * // display_name: 'gadgets', + * // title: 'reddit gadget guide', + * // ... + * // }, + * // Subreddit { + * // display_name: 'sports', + * // title: 'the sportspage of the Internet', + * // ... + * // } + * // ] + */ + getSubscriptions (options: ListingQuery): Promise> { + return this._getListing({uri: 'subreddits/mine/subscriber', qs: options}) + } + + /** + * @summary Gets the authenticated user's unread messages. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing unread items in the user's inbox + * @example + * + * r.getUnreadMessages().then(console.log) + * // => Listing [ + * // PrivateMessage { body: 'hi!', was_comment: false, first_message: null, ... }, + * // Comment { body: 'this is a reply', link_title: 'Yay, a selfpost!', was_comment: true, ... } + * // ] + */ + getUnreadMessages (options?: ListingQuery) { + return this._getListing({uri: 'message/unread', qs: options}) + } + + /** + * @summary Conducts a search of reddit submissions. + * @param {object} options Search options. Can also contain options for the resulting Listing. + * @returns A Listing containing the search results. + * @example + * + * r.search({ + * q: 'Cute kittens', + * subreddit: 'aww', + * sort: 'top' + * }).then(console.log) + * // => Listing [ + * // Submission { domain: 'i.imgur.com', banned_by: null, ... }, + * // Submission { domain: 'imgur.com', banned_by: null, ... }, + * // ... + * // ] + */ + search (options: SearchOptions) { + const subreddit = options.subreddit instanceof snoowrap.objects.Subreddit + ? options.subreddit.display_name + : options.subreddit + delete options.subreddit + const qs = {syntax: 'plain', ...options} + return this._getListing({uri: `${subreddit ? `r/${subreddit}/` : ''}search`, qs}) + } + + /** + * @summary Searches subreddits by title and description. + * @param {object} options Options for the search. May also contain Listing parameters. + * @param {string} options.query The search query + * @returns A Listing containing Subreddits + * @example + * + * r.searchSubreddits({query: 'cookies'}).then(console.log) + * // => Listing [ Subreddit { ... }, Subreddit { ... }, ...] + */ + searchSubreddits (options: any) { + options.q = options.query + return this._getListing({uri: 'subreddits/search', qs: omit(options, 'query')}) + } + + // #endregion + + // #region _getSortedFrontpage + + _getSortedFrontpage ( + sortType: typeof FRONTPAGE_SORTS[number]|'comments', + subredditName?: string, + options: SortedListingQuery = {} + ) { + return this._getListing({uri: (subredditName ? `r/${subredditName}/` : '') + sortType, qs: options}) + } + + /** + * @summary Gets a Listing of best posts. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getBest().then(console.log) + * // => Listing [ + * // Submission { domain: 'imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'pics' }, ... }, + * // Submission { domain: 'i.imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'funny' }, ... }, + * // ... + * // ] + * + * r.getBest({limit: 1}).then(console.log) + * // => Listing [ + * // Submission { domain: 'self.redditdev', banned_by: null, subreddit: Subreddit { display_name: 'redditdev' }, ...} + * // ] + */ + getBest (options?: ListingQuery): Promise> { + return this._getSortedFrontpage('best', undefined, options) + } + + /** + * @summary Gets a Listing of controversial posts. + * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from + * the front page of reddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getControversial('technology').then(console.log) + * // => Listing [ + * // Submission { domain: 'thenextweb.com', banned_by: null, subreddit: Subreddit { display_name: 'technology' }, ... }, + * // Submission { domain: 'pcmag.com', banned_by: null, subreddit: Subreddit { display_name: 'technology' }, ... } + * // ] + */ + getControversial (subredditName?: string, options?: SortedListingQuery): Promise> { + return this._getSortedFrontpage('controversial', subredditName, options) + } + + /** + * @summary Gets a Listing of hot posts. + * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from + * the front page of reddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getHot().then(console.log) + * // => Listing [ + * // Submission { domain: 'imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'pics' }, ... }, + * // Submission { domain: 'i.imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'funny' }, ... }, + * // ... + * // ] + * + * r.getHot('gifs').then(console.log) + * // => Listing [ + * // Submission { domain: 'i.imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'gifs' }, ... }, + * // Submission { domain: 'i.imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'gifs' }, ... }, + * // ... + * // ] + * + * r.getHot('redditdev', {limit: 1}).then(console.log) + * // => Listing [ + * // Submission { domain: 'self.redditdev', banned_by: null, subreddit: Subreddit { display_name: 'redditdev' }, ...} + * // ] + */ + getHot (subredditName?: string, options?: ListingQuery): Promise> { + return this._getSortedFrontpage('hot', subredditName, options) + } + + /** + * @summary Gets a Listing of new posts. + * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from + * the front page of reddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getNew().then(console.log) + * // => Listing [ + * // Submission { domain: 'self.Jokes', banned_by: null, subreddit: Subreddit { display_name: 'Jokes' }, ... }, + * // Submission { domain: 'self.AskReddit', banned_by: null, subreddit: Subreddit { display_name: 'AskReddit' }, ... }, + * // ... + * // ] + * + */ + getNew (subredditName?: string, options?: ListingQuery): Promise> { + return this._getSortedFrontpage('new', subredditName, options) + } + + /** + * @summary Gets a Listing of rising posts. + * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from + * the front page of reddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getRising('technology').then(console.log) + * // => Listing [ + * // Submission { domain: 'thenextweb.com', banned_by: null, subreddit: Subreddit { display_name: 'technology' }, ... }, + * // Submission { domain: 'pcmag.com', banned_by: null, subreddit: Subreddit { display_name: 'technology' }, ... } + * // ] + */ + getRising (subredditName?: string, options?: ListingQuery): Promise> { + return this._getSortedFrontpage('rising', subredditName, options) + } + + /** + * @summary Gets a Listing of top posts. + * @param {string} [subredditName] The subreddit to get posts from. If not provided, posts are fetched from + * the front page of reddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved submissions + * @example + * + * r.getTop({t: 'all', limit: 2}).then(console.log) + * // => Listing [ + * // Submission { domain: 'self.AskReddit', banned_by: null, subreddit: Subreddit { display_name: 'AskReddit' }, ... }, + * // Submission { domain: 'imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'funny' }, ... } + * // ] + * + * r.getTop('AskReddit').then(console.log) + * // => Listing [ + * // Submission { domain: 'self.AskReddit', banned_by: null, subreddit: Subreddit { display_name: 'AskReddit' }, ... }, + * // Submission { domain: 'self.AskReddit', banned_by: null, subreddit: Subreddit { display_name: 'AskReddit' }, ... }, + * // Submission { domain: 'self.AskReddit', banned_by: null, subreddit: Subreddit { display_name: 'AskReddit' }, ... }, + * // ... + * // ] + */ + getTop (subredditName?: string, options?: SortedListingQuery): Promise> { + return this._getSortedFrontpage('top', subredditName, options) + } + + /** + * @summary Gets a Listing of new comments. + * @param {string} [subredditName] The subreddit to get comments from. If not provided, posts are fetched from + * the front page of reddit. + * @param {object} [options] Options for the resulting Listing + * @returns A Listing containing the retrieved comments + * @example + * + * r.getNewComments().then(console.log) + * // => Listing [ + * // Comment { link_title: 'What amazing book should be made into a movie, but hasn\'t been yet?', ... } + * // Comment { link_title: 'How far back in time could you go and still understand English?', ... } + * // ] + */ + getNewComments (subredditName?: string, options?: ListingQuery): Promise> { + return this._getSortedFrontpage('comments', subredditName, options) + } + + // #endregion + + // #region _newObject + + /** + * @summary Mark Modmail conversations as read given the subreddit(s) and state. + * @param subreddits + * @param state selected state to mark as read + * @returns {Promise>} a Listing of ModmailConversations marked as read + * @example + * + * r.bulkReadNewModmail(['AskReddit'], 'all').then(console.log) + * // => Listing [ + * // ModmailConversation { id: '75hxt' }, + * // ModmailConversation { id: '75hxg' } + * // ] + * + * r.bulkReadNewModmail([r.getSubreddit('AskReddit')], 'all').then(console.log) + * // => Listing [ + * // ModmailConversation { id: '75hxt' }, + * // ModmailConversation { id: '75hxg' } + * // ] + */ + async bulkReadNewModmail ( + subreddits: (InstanceType)[]|string[], + state: 'archived'|'appeals'|'highlighted'|'notifications'|'join_requests'|'new'|'inprogress'|'mod'|'all' + ) { + const subredditNames = subreddits.map(s => typeof s === 'string' ? s.replace(/^\/?r\//, '') : s.display_name) + const res = await this._post({url: 'api/mod/conversations/bulk/read', form: { + entity: subredditNames.join(','), + state + }}) + return this._newObject('Listing', { + after: null, + before: null, + children: res.conversation_ids.map((id: string) => this._newObject('ModmailConversation', {id})) + }) as Listing> + } + + /** + * @summary Create a new modmail discussion between moderators + * @param {object} options + * @param {string} options.body Body of the discussion + * @param {string} options.subject Title or subject + * @param {string} options.srName Subreddit name without fullname + * @returns {Promise} the created ModmailConversation + * @example + * + * r.createModeratorDiscussion({ + * body: 'test body', + * subject: 'test subject', + * srName: 'AskReddit' + * }).then(console.log) + * // ModmailConversation { messages: [...], objIds: [...], subject: 'test subject', ... } + */ + async createModmailDiscussion ({ + body, + subject, + srName + }: { + body: string, + subject: string, + srName: string + }) { + const parsedFromSr = srName.replace(/^\/?r\//, ''); // Convert '/r/subreddit_name' to 'subreddit_name' + const res = await this._post({ + url: 'api/mod/conversations', form: { + body, subject, srName: parsedFromSr + } + }) + // _newObject ignores most of the response, no practical way to parse the returned content yet + return this._newObject('ModmailConversation', {id: res.conversation.id}) + } + + + /** + * @summary Gets information on a comment with a given id. + * @param {string} commentId - The base36 id of the comment + * @param {string|null} [submissionId] - The id of the submission that the comment belongs to. The replies + * tree will only be available when providing this param. However you still can fetch it separately + * @param {string} [sort] - Determines how the replies tree should be sorted. One of `confidence, + * top, new, controversial, old, random, qa, live` + * @returns {Comment} An unfetched Comment object for the requested comment + * @example + * + * const comment = r.getComment('c0b6xx0', '92dd8', 'new') + * // => Comment { name: 't1_c0b6xx0', link_id: 't3_92dd8', _sort: 'new' } + * comment.fetch().then(cmt => console.log(cmt.author.name)) + * // => 'Kharos' + */ + getComment (commentId: string, submissionId: string, sort: typeof COMMENT_SORTS[number]) { + return this._newObject('Comment', { + name: addFullnamePrefix(commentId, 't1_'), + link_id: submissionId ? addFullnamePrefix(submissionId, 't3_') : null, + _sort: sort + }) + } + + /** + * Gets a livethread by ID. + * @param {string} threadId The base36 ID of the livethread + * @returns {LiveThread} An unfetched LiveThread object + * @example + * + * r.getLivethread('whrdxo8dg9n0') + * // => LiveThread { id: 'whrdxo8dg9n0' } + * r.getLivethread('whrdxo8dg9n0').nsfw.then(console.log) + * // => false + */ + getLivethread (threadId: string) { + return this._newObject('LiveThread', {id: addFullnamePrefix(threadId, 'LiveUpdateEvent_').slice(16)}) + } + + /** + * @summary Gets information on the requester's own user profile. + * @returns {RedditUser} A RedditUser object corresponding to the requester's profile + * @example + * + * r.getMe().then(console.log); + * // => RedditUser { is_employee: false, has_mail: false, name: 'snoowrap_testing', ... } + */ + async getMe () { + const result = await this._get({url: 'api/v1/me'}) + this._ownUserInfo = this._newObject('RedditUser', result, true) + return this._ownUserInfo! + } + + async _getMyName () { + return this._ownUserInfo ? this._ownUserInfo.name : (await this.getMe()).name + } + + /** + * @summary Gets a private message by ID. + * @param {string} messageId The base36 ID of the message + * @returns {PrivateMessage} An unfetched PrivateMessage object for the requested message + * @example + * + * r.getMessage('51shnw') + * // => PrivateMessage { name: 't4_51shnw' } + * r.getMessage('51shnw').subject.then(console.log) + * // => 'Example' + * // See here for a screenshot of the PM in question https://i.gyazo.com/24f3b97e55b6ff8e3a74cb026a58b167.png + */ + getMessage (messageId: string) { + return this._newObject('PrivateMessage', {name: addFullnamePrefix(messageId, 't4_')}) + } + + /** + * @summary Get a ModmailConversation by its id + * @param {string} id of the ModmailConversation + * @returns {Promise} the requested ModmailConversation + * @example + * + * r.getNewModmailConversation('75hxt').then(console.log) + * // ModmailConversation { messages: [...], objIds: [...], ... } + */ + getNewModmailConversation (id: string) { + return this._newObject('ModmailConversation', {id}) + } + + /** + * @summary Gets all moderated subreddits that have new Modmail activated + * @returns {Promise>} a Listing of ModmailConversations marked as read + * @example + * + * r.getNewModmailSubreddits().then(console.log) + * // => Listing [ + * // Subreddit { display_name: 'tipofmytongue', ... }, + * // Subreddit { display_name: 'EarthPorn', ... }, + * // ] + */ + async getNewModmailSubreddits () { + const response = await this._get({url: 'api/mod/conversations/subreddits'}) + return Object.values(response.subreddits).map(s => this._newObject('Subreddit', s)) + } + + /** + * @summary Gets information on a given submission. + * @param {string} submissionId - The base36 id of the submission + * @param {string} [sort] - Determines how the comments tree should be sorted. One of `confidence, + * top, new, controversial, old, random, qa, live` + * @returns {Submission} An unfetched Submission object for the requested submission + * @example + * + * const submission = r.getSubmission('2np694', 'top') + * // => Submission { name: 't3_2np694', _sort: 'top' } + * submission.fetch().then(sub => console.log(sub.title)) + * // => 'What tasty food would be distusting if eaten over rice?' + */ + getSubmission (submissionId: string, sort?: typeof COMMENT_SORTS[number]) { + return this._newObject('Submission', {name: addFullnamePrefix(submissionId, 't3_'), _sort: sort}) + } + + /** + * @summary Gets information on a given subreddit. + * @param {string} displayName - The name of the subreddit (e.g. 'AskReddit') + * @returns {Subreddit} An unfetched Subreddit object for the requested subreddit + * @example + * + * r.getSubreddit('AskReddit') + * // => Subreddit { display_name: 'AskReddit' } + * r.getSubreddit('AskReddit').created_utc.then(console.log) + * // => 1201233135 + */ + getSubreddit (displayName: string) { + return this._newObject('Subreddit', {display_name: displayName.replace(/^\/?r\//, '')}) + } + + /** + * @summary Gets information on a reddit user with a given name. + * @param {string} name - The user's username + * @returns {RedditUser} An unfetched RedditUser object for the requested user + * @example + * + * r.getUser('not_an_aardvark') + * // => RedditUser { name: 'not_an_aardvark' } + * r.getUser('not_an_aardvark').link_karma.then(console.log) + * // => 6 + */ + getUser (name: string) { + return this._newObject('RedditUser', {name: (name + '').replace(/^\/?u\//, '')}) + } + + // #endregion + + // #region rest + + /** + * @summary Determines whether the currently-authenticated user needs to fill out a captcha in order to submit content. + * @returns A Promise that resolves with a boolean value + * @example + * + * r.checkCaptchaRequirement().then(console.log) + * // => false + */ + checkCaptchaRequirement () { + return this._get({url: 'api/needs_captcha'}) + } + + /** + * @summary Gets the identifier (a hex string) for a new captcha image. + * @returns A Promise that resolves with a string + * @example + * + * r.getNewCaptchaIdentifier().then(console.log) + * // => 'o5M18uy4mk0IW4hs0fu2GNPdXb1Dxe9d' + */ + async getNewCaptchaIdentifier () { + const res = await this._post({url: 'api/new_captcha', form: {api_type}}) + return res.json.data.iden + } + + /** + * @summary Gets an image for a given captcha identifier. + * @param {string} identifier The captcha identifier. + * @returns A string containing raw image data in PNG format + * @example + * + * r.getCaptchaImage('o5M18uy4mk0IW4hs0fu2GNPdXb1Dxe9d').then(console.log) + // => (A long, incoherent string representing the image in PNG format) + */ + getCaptchaImage (identifier: string) { + return this._get({url: `captcha/${identifier}`}) + } + + /** + * @summary Checks whether a given username is available for registration + * @desc **Note:** This function will not work when snoowrap is running in a browser, due to an issue with reddit's CORS + * settings. + * @param {string} name The username in question + * @returns A Promise that fulfills with a Boolean (`true` or `false`) + * @example + * + * r.checkUsernameAvailability('not_an_aardvark').then(console.log) + * // => false + * r.checkUsernameAvailability('eqwZAr9qunx7IHqzWVeF').then(console.log) + * // => true + */ + checkUsernameAvailability (name: string) { + // The oauth endpoint listed in reddit's documentation doesn't actually work, so just send an unauthenticated request. + return this.unauthenticatedRequest({url: 'api/username_available.json', params: {user: name}}) + } + + /** + * @summary Composes a new private message. + * @param {object} options + * @param {RedditUser|Subreddit|string} options.to The recipient of the message. + * @param {string} options.subject The message subject (100 characters max) + * @param {string} options.text The body of the message, in raw markdown text + * @param {Subreddit|string} [options.fromSubreddit] If provided, the message is sent as a modmail from the specified + * subreddit. + * @param {string} [options.captchaIden] A captcha identifier. This is only necessary if the authenticated account + * requires a captcha to submit posts and comments. + * @param {string} [options.captchaResponse] The response to the captcha with the given identifier + * @returns A Promise that fulfills when the request is complete + * @example + * + * r.composeMessage({ + * to: 'actually_an_aardvark', + * subject: "Hi, how's it going?", + * text: 'Long time no see' + * }) + * // (message created on reddit) + */ + async composeMessage ({ + to, + subject, + text, + fromSubreddit, + captchaIden, + captchaResponse + }: { + to: InstanceType|string, + subject: string, + text: string, + fromSubreddit: InstanceType|string, + captchaIden: string, + captchaResponse: string + }) { + let parsedTo = to + let parsedFromSr = fromSubreddit + if (to instanceof snoowrap.objects.RedditUser) { + parsedTo = to.name + } else if (to instanceof snoowrap.objects.Subreddit) { + parsedTo = `/r/${to.display_name}` + } + if (fromSubreddit instanceof snoowrap.objects.Subreddit) { + parsedFromSr = fromSubreddit.display_name + } else if (typeof fromSubreddit === 'string') { + parsedFromSr = fromSubreddit.replace(/^\/?r\//, '') // Convert '/r/subreddit_name' to 'subreddit_name' + } + const result = await this._post({ + url: 'api/compose', form: { + api_type, captcha: captchaResponse, iden: captchaIden, from_sr: parsedFromSr, subject, text, to: parsedTo + } + }) + handleJsonErrors(result) + return result + } + + /** + * @summary Creates a new LiveThread. + * @param {object} options + * @param {string} options.title The title of the livethread (100 characters max) + * @param {string} [options.description] A descriptions of the thread. 120 characters max + * @param {string} [options.resources] Information and useful links related to the thread. 120 characters max + * @param {boolean} [options.nsfw=false] Determines whether the thread is Not Safe For Work + * @returns A Promise that fulfills with the new LiveThread when the request is complete + * @example + * + * r.createLivethread({title: 'My livethread'}).then(console.log) + * // => LiveThread { id: 'wpimncm1f01j' } + */ + async createLivethread ({ + title, + description, + resources, + nsfw = false + }: { + title: string, + description: string, + resources: string, + nsfw: boolean + }) { + const result = await this._post({ + url: 'api/live/create', + form: {api_type, description, nsfw, resources, title} + }) + handleJsonErrors(result) + return this.getLivethread(result.json.data.id) + } + + /** + * @summary Creates a new multireddit. + * @param {object} options + * @param {string} options.name The name of the new multireddit. 50 characters max + * @param {string} options.description A description for the new multireddit, in markdown. + * @param {Array} options.subreddits An Array of Subreddit objects (or subreddit names) that this multireddit should compose of + * @param {string} [options.visibility='private'] The multireddit's visibility setting. One of `private`, `public`, `hidden`. + * @param {string} [options.icon_name=''] One of `art and design`, `ask`, `books`, `business`, `cars`, `comics`, + * `cute animals`, `diy`, `entertainment`, `food and drink`, `funny`, `games`, `grooming`, `health`, `life advice`, `military`, + * `models pinup`, `music`, `news`, `philosophy`, `pictures and gifs`, `science`, `shopping`, `sports`, `style`, `tech`, + * `travel`, `unusual stories`, `video`, `None` + * @param {string} [options.key_color='#000000'] A six-digit RGB hex color, preceded by '#' + * @param {string} [options.weighting_scheme='classic'] One of `classic`, `fresh` + * @returns A Promise for the newly-created MultiReddit object + * @example + * + * r.createMultireddit({ + * name: 'myMulti', + * description: 'An example multireddit', + * subreddits: ['snoowrap', 'snoowrap_testing'] + * }).then(console.log) + * => MultiReddit { display_name: 'myMulti', ... } + */ + createMultireddit ({ + name, + description, + subreddits, + visibility = 'private', + icon_name = '', + key_color = '#000000', + weighting_scheme = 'classic' + }: { + name: string, + description: string, + subreddits: InstanceType[]|string[], + visibility: string, + icon_name: string, + key_color: string, + weighting_scheme:string + }) { + return this._post({ + url: 'api/multi', form: { + model: JSON.stringify({ + display_name: name, + description_md: description, + icon_name, + key_color, + subreddits: subreddits.map(sub => ({name: typeof sub === 'string' ? sub : sub.display_name})), + visibility, + weighting_scheme + }) + } + }) + } + + async _createOrEditSubreddit ({ + name, + title, + public_description, + description, + allow_images = true, + allow_top = true, + captcha, + captcha_iden, + collapse_deleted_comments = false, + comment_score_hide_mins = 0, + exclude_banned_modqueue = false, + 'header-title': header_title, + hide_ads = false, + lang = 'en', + link_type = 'any', + over_18 = false, + public_traffic = false, + show_media = false, + show_media_preview = true, + spam_comments = 'high', + spam_links = 'high', + spam_selfposts = 'high', + spoilers_enabled = false, + sr, + submit_link_label = '', + submit_text_label = '', + submit_text = '', + suggested_comment_sort = 'confidence', + type = 'public', + wiki_edit_age, + wiki_edit_karma, + wikimode = 'modonly', + ...otherKeys + }: SubredditOptions) { + const res = await this._post({ + url: 'api/site_admin', form: { + allow_images, allow_top, api_type, captcha, collapse_deleted_comments, comment_score_hide_mins, description, + exclude_banned_modqueue, 'header-title': header_title, hide_ads, iden: captcha_iden, lang, link_type, name, + over_18, public_description, public_traffic, show_media, show_media_preview, spam_comments, spam_links, + spam_selfposts, spoilers_enabled, sr, submit_link_label, submit_text, submit_text_label, suggested_comment_sort, + title, type, wiki_edit_age, wiki_edit_karma, wikimode, + ...otherKeys + } + }) + handleJsonErrors(res) + return this.getSubreddit(name || sr) + } + + /** + * @summary Creates a new subreddit. + * @param {object} options + * @param {string} options.name The name of the new subreddit + * @param {string} options.title The text that should appear in the header of the subreddit + * @param {string} options.public_description The text that appears with this subreddit on the search page, or on the + * blocked-access page if this subreddit is private. (500 characters max) + * @param {string} options.description The sidebar text for the subreddit. (5120 characters max) + * @param {string} [options.submit_text=''] The text to show below the submission page (1024 characters max) + * @param {boolean} [options.hide_ads=false] Determines whether ads should be hidden on this subreddit. (This is only + * allowed for gold-only subreddits.) + * @param {string} [options.lang='en'] The language of the subreddit (represented as an IETF language tag) + * @param {string} [options.type='public'] Determines who should be able to access the subreddit. This should be one of + * `public, private, restricted, gold_restricted, gold_only, archived, employees_only`. + * @param {string} [options.link_type='any'] Determines what types of submissions are allowed on the subreddit. This should + * be one of `any, link, self`. + * @param {string} [options.submit_link_label=undefined] Custom text to display on the button that submits a link. If + * this is omitted, the default text will be displayed. + * @param {string} [options.submit_text_label=undefined] Custom text to display on the button that submits a selfpost. If + * this is omitted, the default text will be displayed. + * @param {string} [options.wikimode='modonly'] Determines who can edit wiki pages on the subreddit. This should be one of + * `modonly, anyone, disabled`. + * @param {number} [options.wiki_edit_karma=0] The minimum amount of subreddit karma needed for someone to edit this + * subreddit's wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) + * @param {number} [options.wiki_edit_age=0] The minimum account age (in days) needed for someone to edit this subreddit's + * wiki. (This is only relevant if `options.wikimode` is set to `anyone`.) + * @param {string} [options.spam_links='high'] The spam filter strength for links on this subreddit. This should be one of + * `low, high, all`. + * @param {string} [options.spam_selfposts='high'] The spam filter strength for selfposts on this subreddit. This should be + * one of `low, high, all`. + * @param {string} [options.spam_comments='high'] The spam filter strength for comments on this subreddit. This should be one + * of `low, high, all`. + * @param {boolean} [options.over_18=false] Determines whether this subreddit should be classified as NSFW + * @param {boolean} [options.allow_top=true] Determines whether the new subreddit should be able to appear in /r/all and + * trending subreddits + * @param {boolean} [options.show_media=false] Determines whether image thumbnails should be enabled on this subreddit + * @param {boolean} [options.show_media_preview=true] Determines whether media previews should be expanded by default on this + * subreddit + * @param {boolean} [options.allow_images=true] Determines whether image uploads and links to image hosting sites should be + * enabled on this subreddit + * @param {boolean} [options.exclude_banned_modqueue=false] Determines whether posts by site-wide banned users should be + * excluded from the modqueue. + * @param {boolean} [options.public_traffic=false] Determines whether the /about/traffic page for this subreddit should be + * viewable by anyone. + * @param {boolean} [options.collapse_deleted_comments=false] Determines whether deleted and removed comments should be + * collapsed by default + * @param {string} [options.suggested_comment_sort=undefined] The suggested comment sort for the subreddit. This should be + * one of `confidence, top, new, controversial, old, random, qa`. If left blank, there will be no suggested sort, + * which means that users will see the sort method that is set in their own preferences (usually `confidence`.) + * @param {boolean} [options.spoilers_enabled=false] Determines whether users can mark their posts as spoilers + * @returns A Promise for the newly-created subreddit object. + * @example + * + * r.createSubreddit({ + * name: 'snoowrap_testing2', + * title: 'snoowrap testing: the sequel', + * public_description: 'thanks for reading the snoowrap docs!', + * description: 'This text will go on the sidebar', + * type: 'private' + * }).then(console.log) + * // => Subreddit { display_name: 'snoowrap_testing2' } + * // (/r/snoowrap_testing2 created on reddit) + */ + createSubreddit (options: SubredditOptions) { + return this._createOrEditSubreddit(options) + } + + + /** + * @summary Gets the list of people that the currently-authenticated user has blocked. + * @returns A Promise that resolves with a list of blocked users + * @example + * + * r.getBlockedUsers().then(console.log) + * // => [ RedditUser { date: 1457928120, name: 'actually_an_aardvark', id: 't2_q3519' } ] + */ + getBlockedUsers () { + return this._get({url: 'prefs/blocked'}) + } + + /** + * @summary Get list of content by IDs. Returns a listing of the requested content. + * @param ids An array of content IDs. Can include the id itself, or a Submission or Comment object. + * can get a post and a comment + * @returns {Promise>} A listing of content requested, can be any class fetchable by API. e.g. Comment, Submission + * @example + * + * r.getContentByIds(['t3_9l9vof', 't3_9la341']).then(console.log); + * // => Listing [ + * // Submission { approved_at_utc: null, ... } + * // Submission { approved_at_utc: null, ... } + * // ] + * + * r.getContentByIds([r.getSubmission('9l9vof'), r.getSubmission('9la341')]).then(console.log); + * // => Listing [ + * // Submission { approved_at_utc: null, ... } + * // Submission { approved_at_utc: null, ... } + * // ] + */ + getContentByIds (ids: (string|Submission|Comment)[]) { + if (!Array.isArray(ids)) { + throw new TypeError('Invalid argument: Argument needs to be an array.') + } + + const prefixedIds = ids.map(id => { + if (id instanceof snoowrap.objects.Submission || id instanceof snoowrap.objects.Comment) { + return id.name + } else if (typeof id === 'string') { + if (!/t(1|3)_/g.test(id)) { + throw new TypeError('Invalid argument: Ids need to include Submission or Comment prefix, e.g. t1_, t3_.'); + } + return id + } + throw new TypeError('Id must be either a string, Submission, or Comment.') + }) + + return this._get({url: '/api/info', params: {id: prefixedIds.join(',')}}) + } + + /** + * @summary Gets the list of the currently-authenticated user's friends. + * @returns A Promise that resolves with a list of friends + * @example + * + * r.getFriends().then(console.log) + * // => [ [ RedditUser { date: 1457927963, name: 'not_an_aardvark', id: 't2_k83md' } ], [] ] + */ + getFriends () { + return this._get({url: 'prefs/friends'}) + } + + /** + * @summary Gets a distribution of the requester's own karma distribution by subreddit. + * @returns A Promise for an object with karma information + * @example + * + * r.getKarma().then(console.log) + * // => [ + * // { sr: Subreddit { display_name: 'redditdev' }, comment_karma: 16, link_karma: 1 }, + * // { sr: Subreddit { display_name: 'programming' }, comment_karma: 2, link_karma: 1 }, + * // ... + * // ] + */ + getKarma () { + return this._get({url: 'api/v1/me/karma'}) + } + + + + /** + * @summary Gets the user's own multireddits. + * @returns A Promise for an Array containing the requester's MultiReddits. + * @example + * + * r.getMyMultireddits().then(console.log) + * => [ MultiReddit { ... }, MultiReddit { ... }, ... ] + */ + getMyMultireddits () { + return this._get({url: 'api/multi/mine', params: {expand_srs: true}}) + } + + /** + * @summary Gets the currently-authenticated user's trophies. + * @returns A TrophyList containing the user's trophies + * @example + * + * r.getMyTrophies().then(console.log) + * // => TrophyList { trophies: [ + * // Trophy { icon_70: 'https://s3.amazonaws.com/redditstatic/award/verified_email-70.png', + * // description: null, + * // url: null, + * // icon_40: 'https://s3.amazonaws.com/redditstatic/award/verified_email-40.png', + * // award_id: 'o', + * // id: '16fn29', + * // name: 'Verified Email' + * // } + * // ] } + */ + getMyTrophies () { + return this._get({url: 'api/v1/me/trophies'}) + } + + /** + * @summary Gets a list of all oauth scopes supported by the reddit API. + * @desc **Note**: This lists every single oauth scope. To get the scope of this requester, use the `scope` property instead. + * @returns An object containing oauth scopes. + * @example + * + * r.getOauthScopeList().then(console.log) + * // => { + * // creddits: { + * // description: 'Spend my reddit gold creddits on giving gold to other users.', + * // id: 'creddits', + * // name: 'Spend reddit gold creddits' + * // }, + * // modcontributors: { + * // description: 'Add/remove users to approved submitter lists and ban/unban or mute/unmute users from ...', + * // id: 'modcontributors', + * // name: 'Approve submitters and ban users' + * // }, + * // ... + * // } + */ + getOauthScopeList () { + return this._get({url: 'api/v1/scopes'}) + } + + /** + * @summary Gets information on the user's current preferences. + * @returns A promise for an object containing the user's current preferences + * @example + * + * r.getPreferences().then(console.log) + * // => { default_theme_sr: null, threaded_messages: true, hide_downs: false, ... } + */ + getPreferences () { + return this._get({url: 'api/v1/me/prefs'}) + } + + /** + * @summary Gets a single random Submission. + * @desc **Notes**: This function will not work when snoowrap is running in a browser, because the reddit server sends a + * redirect which cannot be followed by a CORS request. Also, due to a known API issue, this function won't work with subreddits + * excluded from /r/all, since the reddit server returns the subreddit itself instead of a random submission, in this case + * the function will return `null`. + * @param {string} [subredditName] The subreddit to get the random submission. If not provided, the post is fetched from + * the front page of reddit. + * @returns {Promise|null} The retrieved Submission object when available + * @example + * + * r.getRandomSubmission('aww').then(console.log) + * // => Submission { domain: 'i.imgur.com', banned_by: null, subreddit: Subreddit { display_name: 'aww' }, ... } + */ + async getRandomSubmission (subredditName: string) { + const res = await this._get({url: `${subredditName ? `r/${subredditName}/` : ''}random`}) + return res instanceof snoowrap.objects.Submission ? res : null + } + + /** + * @summary Gets an array of categories that items can be saved in. (Requires reddit gold) + * @returns An array of categories + * @example + * + * r.getSavedCategories().then(console.log) + * // => [ { category: 'cute cat pictures' }, { category: 'interesting articles' } ] + */ + async getSavedCategories () { + const res = await this._get({url: 'api/saved_categories'}) + return res.categories + } + + /** + * @summary Gets the "happening now" LiveThread, if it exists + * @desc This is the LiveThread that is occasionally linked at the top of reddit.com, relating to current events. + * @returns A Promise that fulfills with the "happening now" LiveThread if it exists, or rejects with a 404 error + * otherwise. + * @example r.getCurrentEventsLivethread().then(thread => thread.stream.on('update', console.log)) + */ + getStickiedLivethread () { + return this._get({url: 'api/live/happening_now'}) + } + + /** + * @summary Represents the unread count in a {@link ModmailConversation}. Each of these properties + * correspond to the amount of unread conversations of that type. + * @typedef {Object} UnreadCount + * @property {number} highlighted + * @property {number} notifications + * @property {number} archived + * @property {number} new + * @property {number} inprogress + * @property {number} mod + */ + + /** + * @summary Retrieves an object of unread Modmail conversations for each state. + * @returns {UnreadCount} unreadCount + * @example + * + * r.getUnreadNewModmailConversationsCount().then(console.log) + * // => { + * // archived: 1, + * // appeals: 1, + * // highlighted: 0, + * // notifications: 0, + * // join_requests: 0, + * // new: 2, + * // inprogress: 5, + * // mod: 1, + * // } + */ + getUnreadNewModmailConversationsCount () { + return this._get({url: 'api/mod/conversations/unread/count'}) + } + + /** + * @summary Marks a list of submissions as 'visited'. + * @desc **Note**: This endpoint only works if the authenticated user is subscribed to reddit gold. + * @param {Submission[]} submission A list of Submission objects to mark + * @returns A Promise that fulfills when the request is complete + * @example + * + * var submissions = [r.getSubmission('4a9u54'), r.getSubmission('4a95nb')] + * r.markAsVisited(submissions) + * // (the links will now appear purple on reddit) + */ + markAsVisited (submission: Submission[]) { + return this._post({url: 'api/store_visits', form: {links: submission.map(sub => sub.name).join(',')}}) + } + + /** + * @summary Marks all of the given messages as read. + * @param {PrivateMessage[]|String[]} messages An Array of PrivateMessage or Comment objects. Can also contain strings + * representing message or comment IDs. If strings are provided, they are assumed to represent PrivateMessages unless a fullname + * prefix such as `t1_` is specified. + * @returns A Promise that fulfills when the request is complete + * @example + * + * r.markMessagesAsRead(['51shsd', '51shxv']) + * + * // To reference a comment by ID, be sure to use the `t1_` prefix, otherwise snoowrap will be unable to distinguish the + * // comment ID from a PrivateMessage ID. + * r.markMessagesAsRead(['t5_51shsd', 't1_d3zhb5k']) + * + * // Alternatively, just pass in a comment object directly. + * r.markMessagesAsRead([r.getMessage('51shsd'), r.getComment('d3zhb5k')]) + */ + markMessagesAsRead (messages: InstanceType[]|string[]) { + const messageIds = messages.map(message => addFullnamePrefix(message, 't4_')) + return this._post({url: 'api/read_message', form: {id: messageIds.join(',')}}) + } + + /** + * @summary Marks all of the given messages as unread. + * @param {PrivateMessage[]|String[]} messages An Array of PrivateMessage or Comment objects. Can also contain strings + * representing message IDs. If strings are provided, they are assumed to represent PrivateMessages unless a fullname prefix such + * as `t1_` is included. + * @returns A Promise that fulfills when the request is complete + * @example + * + * r.markMessagesAsUnread(['51shsd', '51shxv']) + * + * // To reference a comment by ID, be sure to use the `t1_` prefix, otherwise snoowrap will be unable to distinguish the + * // comment ID from a PrivateMessage ID. + * r.markMessagesAsUnread(['t5_51shsd', 't1_d3zhb5k']) + * + * // Alternatively, just pass in a comment object directly. + * r.markMessagesAsRead([r.getMessage('51shsd'), r.getComment('d3zhb5k')]) + */ + markMessagesAsUnread (messages: InstanceType[]|string[]) { + const messageIds = messages.map(message => addFullnamePrefix(message, 't4_')); + return this._post({url: 'api/unread_message', form: {id: messageIds.join(',')}}); + } + + /** + * @summary Marks all conversations in array as read. + * @param {ModmailConversation[]} conversations to mark as read + * @example + * + * r.markNewModmailConversationsAsRead(['pics', 'sweden']) + */ + markNewModmailConversationsAsRead (conversations: InstanceType[]|string[]) { + const conversationIds = conversations.map(message => addFullnamePrefix(message, '')); + return this._post({url: 'api/mod/conversations/read', form: {conversationIds: conversationIds.join(',')}}); + } + + /** + * @summary Marks all conversations in array as unread. + * @param {ModmailConversation[]} conversations to mark as unread + * @example + * + * r.markNewModmailConversationsAsUnread(['pics', 'sweden']) + */ + markNewModmailConversationsAsUnread (conversations: InstanceType[]|string[]) { + const conversationIds = conversations.map(message => addFullnamePrefix(message, '')); + return this._post({url: 'api/mod/conversations/unread', form: {conversationIds: conversationIds.join(',')}}); + } + + /** + * @summary Marks all of the user's messages as read. + * @desc **Note:** The reddit.com site imposes a ratelimit of approximately 1 request every 10 minutes on this endpoint. + * Further requests will cause the API to return a 429 error. + * @returns A Promise that resolves when the request is complete + * @example + * + * r.readAllMessages().then(function () { + * r.getUnreadMessages().then(console.log) + * }) + * // => Listing [] + * // (messages marked as 'read' on reddit) + */ + readAllMessages () { + return this._post({url: 'api/read_all_messages'}); + } + + /** + * @summary Searches for subreddits given a query. + * @param {object} options + * @param {string} options.query A search query (50 characters max) + * @param {boolean} [options.exact=false] Determines whether the results shouldbe limited to exact matches. + * @param {boolean} [options.includeNsfw=true] Determines whether the results should include NSFW subreddits. + * @returns An Array containing subreddit names + * @example + * + * r.searchSubredditNames({query: 'programming'}).then(console.log) + * // => [ + * // 'programming', + * // 'programmingcirclejerk', + * // 'programminghorror', + * // ... + * // ] + */ + async searchSubredditNames ({exact = false, include_nsfw = true, query = ''}) { + const res = await this._post({url: 'api/search_reddit_names', params: {exact, include_over_18: include_nsfw, query}}); + return res.names; + } + + /** + * @summary Updates the user's current preferences. + * @param {object} updatedPreferences An object of the form {[some preference name]: 'some value', ...}. Any preference + * not included in this object will simply retain its current value. + * @returns A Promise that fulfills when the request is complete + * @example + * + * r.updatePreferences({threaded_messages: false, hide_downs: true}) + * // => { default_theme_sr: null, threaded_messages: false, hide_downs: true, ... } + * // (preferences updated on reddit) + */ + updatePreferences (updatedPreferences: any) { + return this._patch({url: 'api/v1/me/prefs', data: updatedPreferences}) + } + + // #endregion + + // #region _submit + + /** + * @summary Convert `markdown` to `richtext_json` format that used on the fancypants editor. This format allows + * to embed inline media on selfposts. + * @param {string} markdown The Markdown text to convert. + * @returns A Promise that fulfills with an object in `richtext_json` format. + * @example + * + * r.convertToFancypants('Hello **world**!').then(console.log) + * // => object {document: Array(1)} + */ + async convertToFancypants (markdown: string) { + const response: Fancypants = await this._post({ + url: 'api/convert_rte_body_format', + form: { + output_mode: 'rtjson', + markdown_text: markdown + } + }) + return response + } + + /** + * @summary Upload media to reddit (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param options An object contains the media file to upload. + * @return A Promise that fulfills with an instance of {@link MediaImg} / {@link MediaVideo} / {@link MediaGif} / {@link MediaFile} + * depending on the value of `options.type`. Or `null` when `options.validateOnly` is set to `true`. + * @example + * + * const blob = await (await fetch("https://example.com/video.mp4")).blob() + * r.uploadMedia({ + * file: blob, + * name: 'video.mp4', + * type: 'gif', + * caption: 'This is a silent video!' + * }).then(console.log) + * // => MediaGif + * + * r.uploadMedia({ + * file: './meme.jpg', + * caption: 'Funny!', + * outboundUrl: 'https://example.com' + * }).then(console.log) + * // => MediaFile + */ + async uploadMedia(options: T): Promise + async uploadMedia(options: T): Promise + async uploadMedia(options: T): Promise]> + async uploadMedia ({file, name, type, caption, outboundUrl, validateOnly = false}: UploadMediaOptions) { + if (isBrowser && !fetch) { + throw new errors.InvalidMethodCallError('Your browser doesn\'t support \'no-cors\' requests') + } + if (isBrowser && typeof file === 'string') { + throw new errors.InvalidMethodCallError('\'options.file\' cannot be a \'string\' on browser') + } + // `File` is an instance of `Blob`, so one check for `Blob` is enough + if (typeof file !== 'string' && !(stream && file instanceof stream.Readable) && !(Blob && file instanceof Blob)) { + throw new errors.InvalidMethodCallError('\'options.file\' must be one of: \'string\', \'stream.Readable\', \'Blob\', or a \'File\'') + } + const parsedFile = typeof file === 'string' ? fs && fs.createReadStream(file) : file + const fileName = typeof file === 'string' ? path.basename(file) : (file as {name?: string}).name || name + if (!fileName) requiredArg('options.name') + const fileExt = path.extname(fileName!).replace('.', '') || 'jpeg' // Default to JPEG + const mimetype = Blob && file instanceof Blob ? file.type || MIME_TYPES[fileExt as keyof typeof MIME_TYPES] : '' + const expectedPrefix = MEDIA_TYPES[type!] + if (expectedPrefix && mimetype.split('/')[0] !== expectedPrefix) { + throw new errors.InvalidMethodCallError(`Expected a MIMETYPE for the file '${fileName}' starting with '${expectedPrefix}' but got '${mimetype}'`) + } + // Todo: The file size should be checked + if (validateOnly) return null + const uploadResponse: UploadResponse = await this._post({ + url: 'api/media/asset.json', + form: { + filepath: fileName, + mimetype + } + }) + const uploadURL = 'https:' + uploadResponse.args.action + const fileDetails = { + fileUrl: uploadURL + '/' + uploadResponse.asset.asset_id, + mediaId: uploadResponse.asset.asset_id, + websocketUrl: uploadResponse.asset.websocket_url, + caption, + outboundUrl + } + const formdata = new FormData() + uploadResponse.args.fields.forEach(item => formdata.append(item.name, item.value)) + formdata.append('file', parsedFile, fileName) + let res + if (isBrowser) { + res = await fetch(uploadURL, { + method: 'post', + mode: 'no-cors', + body: formdata as any + }) + this._debug('Response:', res) + /** + * Todo: Since the response of 'no-cors' requests cannot contain the status code, the uploaded file should be validated + * by setting `fileDetails.fileUrl` as the `src` attribute of an img/video element and listening to the load event. + */ + } else { + const contentLength = await new Promise((resolve, reject) => { + formdata.getLength((err, length) => { + if (err) reject(err) + resolve(length) + }) + }) + res = await this.rawRequest({ + url: uploadURL, + method: 'post', + headers: { + 'user-agent': this.user_agent, + 'content-type': `multipart/form-data; boundary=${formdata.getBoundary()}`, + 'content-length': contentLength + }, + data: formdata, + _r: this + }) + } + let media + switch (type) { + case 'img': + media = new MediaImg(fileDetails) + break + case 'video': + media = new MediaVideo(fileDetails) + break + case 'gif': + media = new MediaGif(fileDetails) + break + default: + media = new MediaFile(fileDetails) + break + } + return media + } + + async _submit ({ + sr, + kind, + title, + url, + video_poster_url, + websocketUrl, + items, + text, + richtext_json, + options, + duration, + crosspost_fullname, + resubmit = true, + sendreplies = true, + nsfw = false, + spoiler = false, + flair_id, + flair_text, + collection_id, + discussion_type, + iden, + captcha, + ...opts + }: Partial) { + let ws: WebSocket + if (websocketUrl) { + ws = new WebSocket(websocketUrl) + await new Promise((resolve, reject) => { + ws.onopen = resolve + ws.onerror = () => reject(new errors.WebSocketError('Websocket error.')) + }) + ws.onerror = null + } + + /** + * Todo: still unsure if `options.resubmit` is supported on gallery/poll submissions + */ + let result + switch (kind) { + case 'gallery': + result = await this._post({ + url: 'api/submit_gallery_post.json', data: { + api_type, sr, title, items, resubmit, sendreplies, nsfw, spoiler, + flair_id, flair_text, collection_id, discussion_type, + iden, captcha, ...opts + } + }) + break + case 'poll': + result = await this._post({ + url: 'api/submit_poll_post', data: { + api_type, sr, title, text, options, duration, resubmit, sendreplies, + nsfw, spoiler, flair_id, flair_text, collection_id, discussion_type, + iden, captcha, ...opts + } + }) + break + default: + result = await this._post({ + url: 'api/submit', form: { + api_type, sr, kind, title, url, video_poster_url, text, richtext_json: JSON.stringify(richtext_json), + crosspost_fullname, resubmit, sendreplies, nsfw, spoiler, flair_id, flair_text, + collection_id, discussion_type, iden, captcha, ...opts + } + }) + break + } + handleJsonErrors(result) + + if (ws!) { + if (ws.readyState !== WebSocket.OPEN) { + throw new errors.WebSocketError('Websocket error. Your post may still have been created.') + } + return new Promise>((resolve, reject) => { + ws.onmessage = event => { + ws.onclose = null + ws.close() + try { + const data = JSON.parse(event.data) + if (data.type === 'failed') reject(new errors.MediaPostFailedError()) + const submissionUrl = data.payload.redirect + const submissionId = SUBMISSION_ID_REGEX.exec(submissionUrl)![1] + resolve(this.getSubmission(submissionId)) + } catch (err) { + reject(err) + } + } + ws.onerror = () => { + reject(new errors.WebSocketError('Websocket error. Your post may still have been created.')) + ws.onclose = null + } + ws.onclose = () => reject(new errors.WebSocketError('Websocket closed. Your post may still have been created.')) + }) + } + return result.json.data.id ? this.getSubmission(result.json.data.id) : null + } + + /** + * @summary Creates a new link submission on the given subreddit. + * @param options An object containing details about the submission. + * @returns The newly-created Submission object. + * @example + * + * r.submitLink({ + * subredditName: 'snoowrap_testing', + * title: 'I found a cool website!', + * url: 'https://google.com' + * }).then(console.log) + * // => Submission { name: 't3_4abnfe' } + * // (new linkpost created on reddit) + */ + submitLink (options: SubmitLinkOptions) { + // Todo: Add `options.url` validation. + return this._submit({...options, kind: 'link'}) + } + + /** + * @summary Submit an image submission to the given subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param options An object containing details about the submission. + * @returns The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const blob = await (await fetch("https://example.com/kittens.jpg")).blob() + * r.submitImage({ + * subredditName: 'snoowrap_testing', + * title: 'Take a look at those cute kittens <3', + * imageFile: blob, // Usage as a `Blob`. + * imageFileName: 'kittens.jpg' + * }).then(console.log) + * // => Submission + * // (new image submission created on reddit) + */ + async submitImage ({imageFile, imageFileName, noWebsockets, ...opts}: SubmitImageOptions) { + let url, websocketUrl + try { + const image = imageFile instanceof MediaImg + ? imageFile + : await this.uploadMedia({ + file: imageFile, + name: imageFileName, + type: 'img' + }) + url = image.fileUrl + websocketUrl = image.websocketUrl + } catch (err) { + throw new Error('An error has occurred with the image file: ' + (err as Error).message) + } + return this._submit({...opts, kind: 'image', url, websocketUrl: noWebsockets ? undefined : websocketUrl}) + } + + /** + * @summary Submit a video or videogif submission to the given subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param options An object containing details about the submission. + * @returns The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const mediaVideo = await r.uploadMedia({ + * file: './video.mp4', + * type: 'video' + * }) + * r.submitVideo({ + * subredditName: 'snoowrap_testing', + * title: 'This is a video!', + * videoFile: mediaVideo, // Usage as a `MediaVideo`. + * thumbnailFile: fs.createReadStream('./thumbnail.png'), // Usage as a `stream.Readable`. + * thumbnailFileName: 'thumbnail.png' + * }).then(console.log) + * // => Submission + * // (new video submission created on reddit) + */ + async submitVideo ({ + videoFile, + videoFileName, + thumbnailFile, + thumbnailFileName, + videogif = false, + noWebsockets, + ...opts + }: SubmitVideoOptions) { + let url, video_poster_url, websocketUrl + const kind = videogif ? 'videogif' : 'video' + + /** + * Imagin you just finished uploading a large video, then oops! you faced this error: "An error has occurred with the thumbnail file"! + * In this case we should validate the thumbnail parameters first to ensure that no accidental uploads will happen. + */ + if (!(thumbnailFile instanceof MediaImg)) { + try { + await this.uploadMedia({ + file: thumbnailFile, + name: thumbnailFileName, + type: 'img', + validateOnly: true + }) + } catch (err) { + throw new Error('An error has occurred with the thumbnail file: ' + (err as Error).message) + } + } + + /** + * Now we are safe to start uploading. If the provided video is invalid, the error can be easly catched. + */ + try { + const video = videoFile instanceof MediaVideo + ? videoFile + : await this.uploadMedia({ + file: videoFile, + name: videoFileName, + type: videogif ? 'gif' : 'video' + }) + url = video.fileUrl + websocketUrl = video.websocketUrl + } catch (err) { + throw new Error('An error has occurred with the video file: ' + (err as Error).message) + } + + try { + const thumbnail = thumbnailFile instanceof MediaImg + ? thumbnailFile + : await this.uploadMedia({ + file: thumbnailFile, + name: thumbnailFileName, + type: 'img' + }) + video_poster_url = thumbnail.fileUrl + } catch (err) { + throw new Error('An error occurred with the thumbnail file: ' + (err as Error).message) + } + + return this._submit({...opts, kind, url, video_poster_url, websocketUrl: noWebsockets ? undefined : websocketUrl}) + } + + /** + * @summary Submit a gallery to the given subreddit. (Undocumented endpoint). + * @desc **NOTE**: This method won't work on browsers that don't support the Fetch API natively since it requires to perform + * a 'no-cors' request which is impossible with the XMLHttpRequest API. + * @param options An object containing details about the submission. + * @returns The newly-created Submission object, or `null` if `options.noWebsockets` is `true`. + * @example + * + * const fileinput = document.getElementById('file-input') + * const files = fileinput.files.map(file => { // Usage as an array of `File`s. + * return { + * imageFile: file, + * caption: file.name + * } + * }) + * const blob = await (await fetch("https://example.com/kittens.jpg")).blob() + * const mediaImg = await r.uploadMedia({ // Usage as a `MediaImg`. + * file: blob, + * type: 'img', + * caption: 'cute :3', + * outboundUrl: 'https://example.com/kittens.html' + * }) + * r.submitGallery({ + * subredditName: 'snoowrap_testing', + * title: 'This is a gallery!', + * gallery: [mediaImg, ...files] + * }).then(console.log) + * // => Submission + * // (new gallery submission created on reddit) + */ + async submitGallery ({gallery, ...opts}: SubmitGalleryOptions) { + /** + * Validate every single gallery item to ensure that no accidental uploads will happen. + */ + await Promise.all(gallery.map(async (item, index) => { + try { + if (item.caption && item.caption.length > 180) { + throw new Error('Caption must be 180 characters or less.') + } + // Todo: Add outboundUrl validation. + if (!(item instanceof MediaImg)) { + await this.uploadMedia({ + file: item.file, + name: item.name, + type: 'img', + validateOnly: true, + caption: item.caption, + outboundUrl: item.outboundUrl + }) + } + } catch (err) { + throw new Error(`An error has occurred with the gallery item at the index ${index}: ` + (err as Error).message) + } + })) + + /** + * Now we are safe to upload. It still depends on network conditions tho, that's why it is recommended to pass the gallery items + * as ready-to-use `MediaImg`s instead. + */ + const items = await Promise.all(gallery.map(async (item, index) => { + try { + if (!(item instanceof MediaImg)) { + item = await this.uploadMedia({ + file: item.file, + name: item.name, + type: 'img', + caption: item.caption, + outboundUrl: item.outboundUrl + }) + } + } catch (err) { + throw new Error(`An error occurred with a gallery item at the index ${index}: ` + (err as Error).message) + } + return { + caption: item.caption, + outbound_url: item.outboundUrl, + media_id: item.mediaId + } + })) + + return this._submit({...opts, kind: 'gallery', items}) + } + + /** + * @summary Creates a new selfpost on the given subreddit. + * @param options An object containing details about the submission. + * @returns The newly-created Submission object. + * @example + * + * const mediaVideo = await r.uploadMedia({ + * file: './video.mp4', + * type: 'video', + * caption: 'Short video!' + * }) + * r.submitSelfpost({ + * subredditName: 'snoowrap_testing', + * title: 'This is a selfpost', + * text: 'This is the text body of the selfpost.\n\nAnd This is an inline image {img} And also a video! {vid}', + * inlineMedia: { + * img: { + * file: './animated.gif', // Usage as a file path. + * type: 'img' + * }, + * vid: mediaVideo + * } + * }).then(console.log) + * // => Submission + * // (new selfpost created on reddit) + */ + async submitSelfpost ({text, inlineMedia, richtext_json, ...opts}: SubmitSelfpostOptions) { + /* eslint-disable require-atomic-updates */ + if (richtext_json) { + text = undefined + } + if (text && inlineMedia) { + const placeholders = Object.keys(inlineMedia) + + // Validate inline media + await Promise.all(placeholders.map(async placeholder => { + if (!text!.includes(`{${placeholder}}`)) { + return + } + if (!(inlineMedia[placeholder] instanceof MediaFile)) { + await this.uploadMedia({ + ...inlineMedia[placeholder] as UploadInlineMediaOptions, + validateOnly: true + }) + } + })) + + // Upload if necessary + await Promise.all(placeholders.map(async placeholder => { + if (!text!.includes(`{${placeholder}}`)) { + return + } + if (!(inlineMedia[placeholder] instanceof MediaFile)) { + inlineMedia[placeholder] = await this.uploadMedia({ + ...inlineMedia[placeholder] as UploadInlineMediaOptions + }) + } + })) + + const body = text.replace(PLACEHOLDER_REGEX, (_m, g1: string) => inlineMedia[g1].toString()) + richtext_json = (await this.convertToFancypants(body)).output + text = undefined + } + return this._submit({...opts, kind: 'self', text, richtext_json}) + /* eslint-enable require-atomic-updates */ + } + + /** + * @summary Submit a poll to the given subreddit. (Undocumented endpoint). + * @param options An object containing details about the submission. + * @returns The newly-created Submission object. + * @example + * + * r.submitPoll({ + * subredditName: 'snoowrap_testing', + * title: 'Survey!', + * text: 'Do you like snoowrap?', + * choices: ['YES!', 'NOPE!'], + * duration: 3 + * }).then(console.log) + * // => Submission + * // (new poll submission created on reddit) + */ + submitPoll (options: SubmitPollOptions) { + return this._submit({...options, kind: 'poll'}) + } + + /** + * @summary Creates a new crosspost submission on the given subreddit + * @desc **NOTE**: To create a crosspost, the authenticated account must be subscribed to the subreddit where + * the crosspost is being submitted, and that subreddit be configured to allow crossposts. + * @param options An object containing details about the submission + * @returns The newly-created Submission object + * @example + * + * r.submitCrosspost({ + * title: 'I found an interesting post', + * originalPost: '6vths0', + * subredditName: 'snoowrap' + * }).then(console.log) + * // => Submission + * // (new crosspost submission created on reddit) + */ + submitCrosspost ({originalPost, ...opts}: SubmitCrosspostOptions) { + const crosspost_fullname = originalPost instanceof snoowrap.objects.Submission + ? originalPost.name + : originalPost + ? addFullnamePrefix(originalPost, 't3_') + : opts.crosspost_fullname + if (!crosspost_fullname) requiredArg('options.originalPost') + return this._submit({...opts, kind: 'crosspost', crosspost_fullname}) + } + // #endregion +} + +if ((typeof module === 'undefined' || !module.parent) && isBrowser) { // check if the code is being run in a browser through browserify, etc. + snoowrap._previousSnoowrap = (self as any)[MODULE_NAME] + ;(self as any)[MODULE_NAME] = snoowrap +} + +export default snoowrap +export {Common, AppAuth, ScriptAuth, CodeAuth, All} diff --git a/src/xhr.js b/src/xhr.js deleted file mode 100644 index c4371837..00000000 --- a/src/xhr.js +++ /dev/null @@ -1,93 +0,0 @@ -/* eslint-env browser */ -import Promise from './Promise.js'; -import url from 'url'; -import {stringify as createQueryString} from 'querystring'; -import {RequestError, StatusCodeError} from './errors.js'; - -// Provide a shim for some of the functionality of the `request-promise` npm package in browsers. -// Previously, snoowrap depended on browserify to package `request-promise` for the browser bundle, and while this worked -// properly, it caused the snoowrap bundle to be very large since `request-promise` contains many dependencies that snoowrap -// doesn't actually need. - -function noop () {} - -function tryParseJson (maybeJson) { - try { - return JSON.parse(maybeJson); - } catch (e) { - return maybeJson; - } -} - -function parseHeaders (headerString) { - return headerString.split('\r\n').filter(line => line).reduce((accumulator, line) => { - const index = line.indexOf(': '); - accumulator[line.slice(0, index)] = line.slice(index + 2); - return accumulator; - }, {}); -} - -module.exports = function rawRequest (options) { - // It would be nice to be able to use the `URL` API in browsers, but Safari 9 doesn't support `URLSearchParams`. - const parsedUrl = url.parse(options.url || url.resolve(options.baseUrl, options.uri), true); - parsedUrl.search = createQueryString(Object.assign({}, parsedUrl.query, options.qs)); - // create a new url object with the new qs params, to ensure that the `href` value changes (to use later for parsing response) - const finalUrl = url.parse(parsedUrl.format()); - const xhr = new XMLHttpRequest(); - const method = options.method ? options.method.toUpperCase() : 'GET'; - xhr.open(method, finalUrl.href); - Object.keys(options.headers) - .filter(header => header.toLowerCase() !== 'user-agent') - .forEach(key => xhr.setRequestHeader(key, options.headers[key])); - if (options.auth) { - xhr.setRequestHeader( - 'Authorization', - options.auth.bearer ? `bearer ${options.auth.bearer}` : 'basic ' + btoa(`${options.auth.user}:${options.auth.pass}`) - ); - } - - let requestBody; - if (options.formData) { - requestBody = new FormData(); - Object.keys(options.formData).forEach(key => requestBody.append(key, options.formData[key])); - if (options.form) { - Object.keys(options.form).forEach(key => requestBody.append(key, options.form[key])); - } - xhr.setRequestHeader('Content-Type', 'multipart/form-data'); - } else if (options.form) { - requestBody = createQueryString(options.form); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - } else if (options.json) { - requestBody = JSON.stringify(options.body); - xhr.setRequestHeader('Content-Type', 'application/json'); - } else { - requestBody = options.body; - } - - return new Promise((resolve, reject, onCancel) => { - onCancel(() => xhr.abort()); - xhr.onload = function () { - const success = this.status >= 200 && this.status < 300; - const settleFunc = success ? resolve : err => reject(Object.assign(new StatusCodeError(this.status + ''), err)); - const response = { - statusCode: this.status, - body: (options.json ? tryParseJson : noop)(xhr.response), - headers: parseHeaders(xhr.getAllResponseHeaders()), - request: {method, uri: finalUrl} - }; - if (typeof options.transform === 'function') { - settleFunc(options.transform(response.body, response)); - } else if (!success || options.resolveWithFullResponse) { - settleFunc(response); - } else { - settleFunc(response.body); - } - }; - xhr.onerror = err => reject(Object.assign(new RequestError(), err)); - xhr.send(requestBody); - }).timeout(options.timeout || Math.pow(2, 31) - 1, 'Error: ETIMEDOUT') - .catch(Promise.TimeoutError, err => { - xhr.abort(); - throw err; - }); -}; diff --git a/test/snoowrap.spec.js b/test/snoowrap.spec.js index 4005f4a4..afaa1716 100644 --- a/test/snoowrap.spec.js +++ b/test/snoowrap.spec.js @@ -5,7 +5,6 @@ const expect = require('chai').use(require('dirty-chai')).expect; const Promise = require('bluebird'); const _ = require('lodash'); const moment = require('moment'); -const request = require('request-promise'); const util = require('util'); const snoowrap = require('..'); @@ -45,24 +44,6 @@ describe('snoowrap', function () { this.retries(3); } r.config({request_delay: 1000}); - - if (!isBrowser) { - const defaultRequest = request.defaults({ - headers: {'user-agent': oauthInfo.user_agent}, - json: true, - jar: request.jar(), - baseUrl: 'https://www.reddit.com/' - }); - - const loginResponse = await defaultRequest.post('api/login').form({ - user: oauthInfo.username, - passwd: oauthInfo.password, - api_type: 'json' - }); - - expect(loginResponse.json.errors.length).to.equal(0); - cookieAgent = defaultRequest.defaults({headers: {'X-Modhash': loginResponse.json.data.modhash}}); - } }); describe('.constructor', () => { diff --git a/tsconfig.json b/tsconfig.json index 1c8565ce..0702b4de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,10 @@ { "compilerOptions": { - "module": "commonjs", - "target": "es6", - "lib": [ - "es2017", - "dom" - ], + "target": "es2017", + "lib": ["es2017", "dom"], + "module": "es2015", + "moduleResolution": "node", + "esModuleInterop": true, "noImplicitAny": true, "noImplicitThis": true, "strict": true, diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 00000000..0f87a65a --- /dev/null +++ b/vite.config.js @@ -0,0 +1,15 @@ +var path = require('path') +var noop = path.resolve('./noop.js') + +/** @type {import('vite').UserConfig} */ +module.exports = { + resolve: { + alias: { + fs: noop, + path: noop, + stream: noop, + url: noop, + ws: noop + } + } +}