diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..0b4dabfa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +end_of_line = crlf +insert_final_newline = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..567e6740 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +/dist +/docs +/node_modules diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..2d396ced --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,345 @@ +// +// .eslintrc.js +// +// Created by David Rowe on 24 Nov 2020. +// Copyright 2020 Vircadia contributors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +module.exports = { + + root: true, + + parser: "@typescript-eslint/parser", + parserOptions: { + "tsconfigRootDir": __dirname, + "project": ["./tsconfig.json"], + "ecmaVersion": 2020, + "sourceType": "module" + }, + + plugins: [ + "@typescript-eslint", + "eslint-plugin-jest" + ], + + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:jest/recommended", // eslint-plugin-jest + "plugin:jest/style" // eslint-plugin-jest + ], + + env: { + "es2020": true, + "node": true + }, + + settings: { + "jest": { + "version": 27 + } + }, + + rules: { + + /* eslint-disable @typescript-eslint/no-magic-numbers */ + + // typescript-eslint 4.28.3 + // TODO: Review rules. + // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin + "brace-style": "off", + "@typescript-eslint/brace-style": ["error"], + "comma-dangle": "off", + "@typescript-eslint/comma-dangle": ["error", "never"], + "comma-spacing": "off", + "@typescript-eslint/comma-spacing": ["error"], + "default-param-last": "off", + "@typescript-eslint/default-param-last": ["error"], + "dot-notation": "off", + "@typescript-eslint/dot-notation": ["error", { "allowKeywords": false }], + "func-call-spacing": "off", + "@typescript-eslint/func-call-spacing": ["error", "never"], + "indent": "off", + // Warning: https://github.com/typescript-eslint/typescript-eslint/issues/1824 + "@typescript-eslint/indent": ["error", 4, { "SwitchCase": 1, "outerIIFEBody": 1 }], + "init-declarations": "off", + "@typescript-eslint/init-declarations": ["error", "always"], + "keyword-spacing": "off", + "@typescript-eslint/keyword-spacing": ["error"], + "lines-between-class-members": "off", + "@typescript-eslint/lines-between-class-members": ["error", "always", { "exceptAfterSingleLine": true }], + "no-array-constructor": "off", + "@typescript-eslint/no-array-constructor": ["error"], + "no-dupe-class-members": "off", + "@typescript-eslint/no-dupe-class-members": ["error"], + "no-duplicate-imports": "off", + "@typescript-eslint/no-duplicate-imports": ["error"], + "no-empty-function": "off", + "@typescript-eslint/no-empty-function": ["error"], + "no-extra-parens": "off", + "@typescript-eslint/no-extra-parens": ["error"], + "no-extra-semi": "off", + "@typescript-eslint/no-extra-semi": ["error"], + "@typescript-eslint/no-floating-promises": ["error", { "ignoreIIFE": true }], + "no-implied-eval": "off", + "@typescript-eslint/no-implied-eval": ["error"], + "no-invalid-this": "off", + "@typescript-eslint/no-invalid-this": ["error"], + "no-loop-func": "off", + "@typescript-eslint/no-loop-func": ["error"], + "no-loss-of-precision": "off", + "@typescript-eslint/no-loss-of-precision": ["error"], + "no-magic-numbers": "off", + "@typescript-eslint/no-magic-numbers": [ + "error", + { + "ignore": [-1, 0, 1, 2], + "ignoreEnums": true, + "ignoreNumericLiteralTypes": true, + "ignoreReadonlyClassProperties": true + } + ], + "no-redeclare": "off", + "@typescript-eslint/no-redeclare": [ + "error", + { + "builtinGlobals": false, + "ignoreDeclarationMerge": true + } + ], + "no-shadow": "off", + "@typescript-eslint/no-shadow": [ + "error", + { + "builtinGlobals": false, + "ignoreTypeValueShadow": true, + "ignoreFunctionTypeParameterNameValueShadow": true + } + ], + "no-throw-literal": "off", + "@typescript-eslint/no-throw-literal": ["error"], + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": ["error"], + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error"], + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": ["error"], + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": ["error"], + "object-curly-spacing": "off", + "@typescript-eslint/object-curly-spacing": ["error", "always"], + "quotes": "off", + "@typescript-eslint/quotes": ["error", "double", { "allowTemplateLiterals": true }], + "require-await": "off", + "@typescript-eslint/require-await": ["error"], + "no-return-await": "off", + "@typescript-eslint/return-await": ["error"], + "semi": "off", + "@typescript-eslint/semi": ["error", "always"], + "space-before-function-paren": "off", + "@typescript-eslint/space-before-function-paren": [ + "error", + { + "anonymous": "always", + "named": "never", + "asyncArrow": "always" + } + ], + "space-infix-ops": "off", + "@typescript-eslint/space-infix-ops": ["error", { "int32Hint": false }], + "@typescript-eslint/type-annotation-spacing": ["error"], + + + // eslint 6.8 + // Excluding TypeScript extension rules that replace desired ESLint rules. + // TODO: Review rules and update to eslint 7.30.0. + // https://eslint.org/docs/rules/ + + // "no-extra-semi": "error", // TypeScript extension overrides. + + // Possible errors. + "no-await-in-loop": "error", + // "no-console": "error", + // "no-extra-parens": ["error", "functions"], // TypeScript extension overrides. + // "no-loss-of-precision": "error", // TypeScript extension overrides. + "no-promise-executor-return": "error", + "no-template-curly-in-string": "error", + "no-unreachable-loop": "error", + "no-unsafe-optional-chaining": "error", + "no-useless-backreference": "error", + "require-atomic-updates": "error", + + // Best practices. + // "accessor-pairs": "error", // There are many places where there are not pairs. + "array-callback-return": "error", + "block-scoped-var": "error", + "class-methods-use-this": "error", + "consistent-return": "error", + "curly": "error", + "default-case": "error", + "default-case-last": "error", + // "default-param-last": "error", // TypeScript extension overrides. + "dot-location": ["error", "property"], + // "dot-notation": ["error", { "allowKeywords": false }], // TypeScript extension overrides. + "eqeqeq": "error", + "grouped-accessor-pairs": "error", + "guard-for-in": "error", + "no-caller": "error", + "no-constructor-return": "error", + "no-else-return": ["error", { allowElseIf: false }], + // "no-empty-function": "error", // TypeScript extension overrides. + "no-eval": ["error"], + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-label": "error", + "no-floating-decimal": "error", + "no-implicit-coercion": "error", + "no-implicit-globals": "error", + // "no-implied-eval": ["error"], // TypeScript extension overrides. + // "no-invalid-this": "error", // TypeScript extension overrides. + "no-iterator": "error", + "no-labels": "error", + "no-lone-blocks": "error", + // "no-loop-func": "error", // TypeScript extension overrides. + // "no-magic-numbers": ["error", { "ignore": [-1, 0, 1, 2] }], // TypeScript extension overrides. + "no-multi-spaces": ["error", { ignoreEOLComments: true }], + "no-multi-str": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-param-reassign": "error", + "no-proto": "error", + "no-return-assign": "error", + // "no-return-await": "error", // TypeScript extension overrides. + "no-script-url": "error", + "no-self-compare": "error", + "no-sequences": "error", + // "no-throw-literal": "error", // TypeScript extension overrides. + "no-unmodified-loop-condition": "error", + // "no-unused-expressions": "error", // TypeScript extension overrides. + "no-useless-call": "error", + "no-useless-concat": "error", + "no-useless-return": "error", + "no-void": ["error", { "allowAsStatement": true }], + "prefer-named-capture-group": "error", + "prefer-promise-reject-errors": "error", + "prefer-regex-literals": "error", + "radix": "error", + // "require-await": "error", // TypeScript extension overrides. + "require-unicode-regexp": "error", + "vars-on-top": "error", + "wrap-iife": ["error", "outside"], + // "yoda": "error", // Want to use ranges without needing extra parentheses. + + // Strict mode. + "strict": ["error", "safe"], + + // Variables + // "init-declarations": ["error", "always"], // TypeScript extension overrides. + "no-label-var": "error", + "no-restricted-globals": ["error", "event", "fdescribe"], + // "no-shadow": ["error", { "builtinGlobals": false }], // TypeScript extension overrides. + // "no-use-before-define": "error", // TypeScript extension overrides. + + // Stylistic issues. + "array-bracket-newline": ["error", { "multiline": true }], + "array-bracket-spacing": ["error", "never"], + "array-element-newline": ["error", "consistent"], + "block-spacing": "error", + // "brace-style": "error", // TypeScript extension overrides. + "camelcase": "error", + // "comma-dangle": ["error", "never"], // TypeScript extension overrides. + // "comma-spacing": "error", // TypeScript extension overrides. + "comma-style": "error", + "computed-property-spacing": "error", + "consistent-this": ["error", "self"], + "eol-last": "error", + // "func-call-spacing": ["error", "never"], // TypeScript extension overrides. + "func-style": ["error", "declaration", { "allowArrowFunctions": true }], + "implicit-arrow-linebreak": ["error", "beside"], + // "indent": ["error", 4, { "SwitchCase": 1, "outerIIFEBody": 1 }], // TypeScript extension overrides. + "jsx-quotes": ["error", "prefer-double"], + "key-spacing": "error", + // "keyword-spacing": "error", // TypeScript extension overrides. + "max-len": ["error", { "code": 128, "tabWidth": 4 }], + "multiline-ternary": ["error", "always-multiline"], + "new-cap": "error", + "new-parens": "error", + "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }], + // "no-array-constructor": "error", // TypeScript extension overrides. + "no-continue": "error", + "no-lonely-if": "error", + "no-multiple-empty-lines": ["error", { "max": 2, "maxBOF": 0, "maxEOF": 0 }], + "no-nested-ternary": "error", + "no-new-object": "error", + "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], + "no-tabs": "error", + "no-trailing-spaces": "error", + "no-unneeded-ternary": "error", + "no-whitespace-before-property": "error", + "object-curly-newline": "error", + // "object-curly-spacing": ["error", "always"], // TypeScript extension overrides. + "one-var": ["error", "never"], + "operator-linebreak": ["error", "before"], + "prefer-exponentiation-operator": "error", + "prefer-object-spread": "error", + // "quotes": ["error", "double", { "allowTemplateLiterals": true }], // TypeScript extension overrides. + // "semi": ["error", "always"], + "semi-spacing": "error", + "semi-style": "error", + "space-before-blocks": "error", + // "space-before-function-paren": [ + // "error", { + // "anonymous": "always", + // "named": "never", + // "asyncArrow": "always" + // } + // ], // TypeScript extension overrides. + "space-in-parens": "error", + // "space-infix-ops": "error", // TypeScript extension overrides. + "space-unary-ops": [ + "error", { + "words": true, + "nonwords": false + } + ], + "spaced-comment": ["error", "always", { "exceptions": ["@devdoc", "@sdkdoc"] }], + "switch-colon-spacing": "error", + "template-tag-spacing": "error", + "unicode-bom": "error", + "wrap-regex": "error", + + // ECMAScript 6 + "arrow-body-style": ["error", "always"], + "arrow-parens": "error", + "arrow-spacing": "error", + "generator-star-spacing": "error", + "no-confusing-arrow": "error", + // "no-duplicate-imports": "error", // TypeScript extension overrides. + "no-useless-computed-key": "error", + // "no-useless-constructor": "error", // TypeScript extension overrides. + "no-useless-rename": "error", + "no-var": "error", + "object-shorthand": ["error", "properties"], + "prefer-const": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "rest-spread-spacing": "error", + "symbol-description": "error", + "template-curly-spacing": "error", + "yield-star-spacing": "error", + + + // eslint-plugin-jest + // Rules extra or exceptions to those in plugin:jest/recommended and plugin:jest/style. + // https://www.npmjs.com/package/eslint-plugin-jest + // TODO: Review rules. + "jest/no-done-callback": "off" + + } +}; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..b971af6d --- /dev/null +++ b/jest.config.js @@ -0,0 +1,11 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest/presets/js-with-ts', + testEnvironment: 'node', + moduleNameMapper:{ + "^@Base/(.*)$": "/src/$1", + "^@Entities/(.*)$": "/src/Entities/$1", + "^@Tools/(.*)$": "/src/Tools/$1", + "^@Route-Tools/(.*)$": "/src/route-tools/$1" + } +}; diff --git a/package-lock.json b/package-lock.json index 7b686b0f..14f9772f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,864 +1,14222 @@ { "name": "iamus-metaverse-server", - "version": "2.4.9", - "lockfileVersion": 1, + "version": "2.4.10", + "lockfileVersion": 2, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" + "packages": { + "": { + "name": "iamus-metaverse-server", + "version": "2.4.10", + "license": "Apache-2.0", + "dependencies": { + "cors": "^2.8.5", + "debug": "~2.6.9", + "deepmerge": "^4.2.2", + "express": "~4.16.1", + "fs-extra": "^9.1.0", + "glob": "^7.1.7", + "http-errors": "~1.6.3", + "loglevel": "^1.7.1", + "module-alias": "^2.2.2", + "mongodb": "^3.6.6", + "morgan": "~1.9.1", + "multer": "^1.4.2", + "nodemailer": "^6.6.0", + "unique-names-generator": "^4.5.0", + "uuid": "^8.3.2", + "winston": "^3.3.3" + }, + "devDependencies": { + "@types/cors": "^2.8.10", + "@types/debug": "^4.1.5", + "@types/express": "^4.17.11", + "@types/glob": "^7.1.3", + "@types/http-errors": "^1.8.0", + "@types/jest": "^27.0.3", + "@types/mongodb": "^3.6.12", + "@types/morgan": "^1.9.2", + "@types/multer": "^1.4.5", + "@types/node": "^14.14.44", + "@types/nodemailer": "^6.4.1", + "@types/uuid": "^8.3.0", + "@typescript-eslint/eslint-plugin": "^5.8.1", + "eslint-plugin-jest": "^25.3.2", + "jest": "^27.4.5", + "npm-run-all": "^4.1.5", + "ts-jest": "^27.1.2", + "tslint": "^6.1.3", + "typescript": "^3.9.9" + }, + "engines": { + "node": ">= 14.18.1", + "npm": ">= 6.14.15" } }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "node_modules/@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "dependencies": { + "@babel/highlight": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "node_modules/@babel/compat-data": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", + "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" + "engines": { + "node": ">=6.9.0" } }, - "@types/bson": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", - "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", + "node_modules/@babel/core": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", + "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", "dev": true, - "requires": { - "@types/node": "*" + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-compilation-targets": "^7.16.3", + "@babel/helper-module-transforms": "^7.16.5", + "@babel/helpers": "^7.16.5", + "@babel/parser": "^7.16.5", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.5", + "@babel/types": "^7.16.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@types/connect": { - "version": "3.4.34", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", - "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "node_modules/@babel/core/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": { - "@types/node": "*" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "@types/cors": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", - "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", + "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 }, - "@types/debug": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", - "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", - "dev": true + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "@types/express": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", - "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "node_modules/@babel/core/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, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" + "engines": { + "node": ">=0.10.0" } }, - "@types/express-serve-static-core": { - "version": "4.17.18", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz", - "integrity": "sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA==", + "node_modules/@babel/generator": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", + "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" + "dependencies": { + "@babel/types": "^7.16.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" + "engines": { + "node": ">=0.10.0" } }, - "@types/http-errors": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", - "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==", - "dev": true + "node_modules/@babel/helper-compilation-targets": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", + "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", + "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@types/mongodb": { - "version": "3.6.12", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.12.tgz", - "integrity": "sha512-49aEzQD5VdHPxyd5dRyQdqEveAg9LanwrH8RQipnMuulwzKmODXIZRp0umtxi1eBUfEusRkoy8AVOMr+kVuFog==", + "node_modules/@babel/helper-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", "dev": true, - "requires": { - "@types/bson": "*", - "@types/node": "*" + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/morgan": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.2.tgz", - "integrity": "sha512-edtGMEdit146JwwIeyQeHHg9yID4WSolQPxpEorHmN3KuytuCHyn2ELNr5Uxy8SerniFbbkmgKMrGM933am5BQ==", + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", "dev": true, - "requires": { - "@types/node": "*" + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/multer": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.5.tgz", - "integrity": "sha512-9b/0a8JyrR0r2nQhL73JR86obWL7cogfX12augvlrvcpciCo/hkvEsgu80Z4S2g2DHGVXHr8pUIi1VhqFJ8Ufw==", + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", "dev": true, - "requires": { - "@types/express": "*" + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/node": { - "version": "14.14.44", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.44.tgz", - "integrity": "sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==", - "dev": true + "node_modules/@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@types/nodemailer": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.1.tgz", - "integrity": "sha512-8081UY/0XTTDpuGqCnDc8IY+Q3DSg604wB3dBH0CaZlj4nZWHWuxtZ3NRZ9c9WUrz1Vfm6wioAUnqL3bsh49uQ==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz", + "integrity": "sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ==", "dev": true, - "requires": { - "@types/node": "*" + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.5", + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.5", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/qs": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.5.tgz", - "integrity": "sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ==", - "dev": true + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz", + "integrity": "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", - "dev": true + "node_modules/@babel/helper-simple-access": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@types/serve-static": { - "version": "1.13.9", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", - "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@types/uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", - "dev": true + "node_modules/@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "node_modules/@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true, + "engines": { + "node": ">=6.9.0" } }, - "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==", + "node_modules/@babel/helpers": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", + "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", "dev": true, - "requires": { - "color-convert": "^1.9.0" + "dependencies": { + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.5", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + "node_modules/@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@babel/parser": { + "version": "7.16.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", + "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "bson": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", - "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.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==" + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.5.tgz", + "integrity": "sha512-/d4//lZ1Vqb4mZ5xTep3dDK888j7BGM/iKqBmndBaoYAFPlPKrGU608VVBz5JeyAb6YQDjRu1UKqj86UhwWVgw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.5" }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", + "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-environment-visitor": "^7.16.5", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.5", + "@babel/types": "^7.16.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/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 } } }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "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 }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "dependencies": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" + "node_modules/@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "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" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "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, + "peer": true }, - "color-string": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.6.0.tgz", - "integrity": "sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "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, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" } }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "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, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "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, + "peer": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "node_modules/@eslint/eslintrc/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, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "dev": true, + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } }, - "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==" + "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, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + "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, + "peer": true }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "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, + "peer": 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/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" } }, - "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/@jest/console": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.4.2.tgz", + "integrity": "sha512-xknHThRsPB/To1FUbi6pCe43y58qFC03zfb6R7fDb/FfC7k2R3i1l+izRBJf8DI46KhYGRaF14Eo9A3qbBoixg==", "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": { + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.4.2", + "jest-util": "^27.4.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" + "node_modules/@jest/console/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" } }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "node_modules/@jest/console/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" + } }, - "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/@jest/console/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, - "requires": { - "object-keys": "^1.0.12" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + "node_modules/@jest/console/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 }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "node_modules/@jest/console/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" + } }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" + "node_modules/@jest/console/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/@jest/core": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.4.5.tgz", + "integrity": "sha512-3tm/Pevmi8bDsgvo73nX8p/WPng6KWlCyScW10FPEoN1HU4pwI83tJ3TsFvi1FfzsjwUlMNEPowgb/rPau/LTQ==", + "dev": true, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "@jest/console": "^27.4.2", + "@jest/reporters": "^27.4.5", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^27.4.2", + "jest-config": "^27.4.5", + "jest-haste-map": "^27.4.5", + "jest-message-util": "^27.4.2", + "jest-regex-util": "^27.4.0", + "jest-resolve": "^27.4.5", + "jest-resolve-dependencies": "^27.4.5", + "jest-runner": "^27.4.5", + "jest-runtime": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "jest-watcher": "^27.4.2", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "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/@jest/core/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": { - "is-arrayish": "^0.2.1" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "node_modules/@jest/core/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, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=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==", + "node_modules/@jest/core/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, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "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 - }, - "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/@jest/core/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 }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "node_modules/@jest/core/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" } }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + "node_modules/@jest/core/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" + } }, - "fecha": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", - "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + "node_modules/@jest/environment": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.4.4.tgz", + "integrity": "sha512-q+niMx7cJgt/t/b6dzLOh4W8Ef/8VyKG7hxASK39jakijJzbFBGpptx3RXz13FFV7OishQ9lTbv+dQ5K3EhfDQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "jest-mock": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" + "node_modules/@jest/fake-timers": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.2.tgz", + "integrity": "sha512-f/Xpzn5YQk5adtqBgvw1V6bF8Nx3hY0OIRRpCvWcfPl0EAjdqWPdhH3t/3XpiWZqtjIEHDyMKP9ajpva1l4Zmg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.4.2", + "jest-mock": "^27.4.2", + "jest-util": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + "node_modules/@jest/globals": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.4.4.tgz", + "integrity": "sha512-bqpqQhW30BOreXM8bA8t8JbOQzsq/WnPTnBl+It3UxAD9J8yxEAaBEylHx1dtBapAr/UBk8GidXbzmqnee8tYQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.4.4", + "@jest/types": "^27.4.2", + "expect": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "node_modules/@jest/reporters": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.5.tgz", + "integrity": "sha512-3orsG4vi8zXuBqEoy2LbnC1kuvkg1KQUgqNxmxpQgIOQEPeV0onvZu+qDQnEoX8qTQErtqn/xzcnbpeTuOLSiA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.4.2", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^27.4.5", + "jest-resolve": "^27.4.5", + "jest-util": "^27.4.2", + "jest-worker": "^27.4.5", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "node_modules/@jest/reporters/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" + } }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "node_modules/@jest/reporters/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" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "node_modules/@jest/reporters/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" + } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "node_modules/@jest/reporters/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 }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "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" + "node_modules/@jest/reporters/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" } }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/@jest/reporters/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": { - "function-bind": "^1.1.1" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "node_modules/@jest/source-map": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz", + "integrity": "sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.2.tgz", + "integrity": "sha512-kr+bCrra9jfTgxHXHa2UwoQjxvQk3Am6QbpAiJ5x/50LW8llOYrxILkqY0lZRW/hu8FXesnudbql263+EW9iNA==", + "dev": true, + "dependencies": { + "@jest/console": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.5.tgz", + "integrity": "sha512-n5woIn/1v+FT+9hniymHPARA9upYUmfi5Pw9ewVwXCDlK4F5/Gkees9v8vdjGdAIJ2MPHLHodiajLpZZanWzEQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.4.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-runtime": "^27.4.5" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.4.5.tgz", + "integrity": "sha512-PuMet2UlZtlGzwc6L+aZmR3I7CEBpqadO03pU40l2RNY2fFJ191b9/ITB44LNOhVtsyykx0OZvj0PCyuLm7Eew==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.4.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-regex-util": "^27.4.0", + "jest-util": "^27.4.2", + "micromatch": "^4.0.4", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/transform/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/@jest/transform/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/@jest/transform/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/@jest/transform/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 }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "node_modules/@jest/transform/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/@jest/transform/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/@jest/types": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", + "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jest/types/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/@jest/types/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/@jest/types/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/@jest/types/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 }, - "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==", + "node_modules/@jest/types/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/@jest/types/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/@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, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "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, + "engines": { + "node": ">= 8" + } + }, + "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, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.17", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.17.tgz", + "integrity": "sha512-6zzkezS9QEIL8yCBvXWxPTJPNuMeECJVxSOhxNY/jfq9LxOTHivaYTqr37n9LknWWRTIkzqH2UilS5QFvfa90A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bson": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz", + "integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==", + "deprecated": "This is a stub types definition. bson provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "bson": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dev": true, + "dependencies": { + "@types/ms": "*" } }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "node_modules/@types/express-serve-static-core": { + "version": "4.17.26", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz", + "integrity": "sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" } }, - "inherits": { + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "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==" + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.0.3", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.3.tgz", + "integrity": "sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg==", + "dev": true, + "dependencies": { + "jest-diff": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "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 }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "dev": true, + "dependencies": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "node_modules/@types/morgan": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz", + "integrity": "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "node_modules/@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "14.18.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.2.tgz", + "integrity": "sha512-fqtSN5xn/bBzDxMT77C1rJg6CsH/R49E7qsGuvdPJa20HtV5zSTuLJPNfnlyVH3wauKnkHdLggTVkOW/xP9oQg==", "dev": true }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "node_modules/@types/nodemailer": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.4.tgz", + "integrity": "sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.2.tgz", + "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", + "integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", + "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "5.8.1", + "@typescript-eslint/scope-manager": "5.8.1", + "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 + } + } + }, + "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, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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/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/@typescript-eslint/eslint-plugin/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/@typescript-eslint/experimental-utils": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", + "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", + "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" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", + "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", + "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 + } + } + }, + "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, + "peer": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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, + "peer": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", + "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", + "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", + "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1", + "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 + } + } + }, + "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, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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 + }, + "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, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/visitor-keys": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", + "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.8.1", + "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" + } + }, + "node_modules/abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/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/agent-base/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/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.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/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/babel-jest": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.5.tgz", + "integrity": "sha512-3uuUTjXbgtODmSv/DXO9nZfD52IyC2OYTFaXGRzL0kpykzroaquCrD5+lZNafTvZlnNqZHt5pb0M08qVBZnsnA==", + "dev": true, + "dependencies": { + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^27.4.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/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/babel-jest/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/babel-jest/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/babel-jest/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/babel-jest/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/babel-jest/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/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz", + "integrity": "sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz", + "integrity": "sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^27.4.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dependencies": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/bson": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.0.tgz", + "integrity": "sha512-8jw1NU1hglS+Da1jDOUYuNcBJ4cNHCFIqzlwoFNnsTOg2R/ox0aTYcTiBN4dzRa9q7Cvy6XErh3L8ReTEb9AQQ==", + "dev": true, + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "dependencies": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/busboy/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/busboy/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/busboy/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=" + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" + } + }, + "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": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001292", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", + "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": 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=" + }, + "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==", + "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/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "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=" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "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==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-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==", + "engines": { + "node": ">=0.10.0" + } + }, + "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/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "dependencies": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/dicer/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/dicer/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/dicer/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=" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", + "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.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/dir-glob/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/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.26", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.26.tgz", + "integrity": "sha512-cA1YwlRzO6TGp7yd3+KAqh9Tt6Z4CuuKqsAJP6uF/H5MQryjAGDhMhnY5cEXo8MaRCczpzSBhMPdqRIodkbZYw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/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/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/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "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/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", + "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.5", + "@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", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.2.0", + "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": "^4.0.6", + "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", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "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-plugin-jest": { + "version": "25.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.3.2.tgz", + "integrity": "sha512-1aPC7RRJkMCNgklHMDECw8fnzag3JBH53LaxmFkDTR7+PfMCO5V6f8XFRHoT2I+Fr4pVO9cPdRGlf7/haB2O5Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "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, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "peer": 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, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "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, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/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, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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, + "peer": true + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": 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/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, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/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, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "peer": 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, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/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, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/espree": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "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/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/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/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/execa/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/execa/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/execa/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/execa/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/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.2.tgz", + "integrity": "sha512-BjAXIDC6ZOW+WBFNg96J22D27Nq5ohn+oGcuP2rtOtcjuxNoV9McpQ60PcQWhdFOSBIQdR72e+4HdnbZTFSTyg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "ansi-styles": "^5.0.0", + "jest-get-type": "^27.4.0", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-regex-util": "^27.4.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/expect/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dependencies": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "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, + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "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" + } + }, + "node_modules/fast-glob/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/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/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + }, + "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, + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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/finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "peer": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true, + "peer": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "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-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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/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, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "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-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.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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 + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/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/http-proxy-agent/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/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/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/https-proxy-agent/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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/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, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/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, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "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/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/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/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==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/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-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-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/is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/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": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "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/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": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.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/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "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/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/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/istanbul-lib-report/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/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/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/istanbul-lib-source-maps/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/istanbul-reports": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz", + "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.5.tgz", + "integrity": "sha512-uT5MiVN3Jppt314kidCk47MYIRilJjA/l2mxwiuzzxGUeJIvA8/pDaJOAX5KWvjAo7SCydcW0/4WEtgbLMiJkg==", + "dev": true, + "dependencies": { + "@jest/core": "^27.4.5", + "import-local": "^3.0.2", + "jest-cli": "^27.4.5" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz", + "integrity": "sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.5.tgz", + "integrity": "sha512-eTNWa9wsvBwPykhMMShheafbwyakcdHZaEYh5iRrQ0PFJxkDP/e3U/FvzGuKWu2WpwUA3C3hPlfpuzvOdTVqnw==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.4.4", + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.4.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.4.2", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-runtime": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "pretty-format": "^27.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-circus/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/jest-circus/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/jest-circus/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/jest-circus/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/jest-circus/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/jest-circus/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/jest-cli": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.5.tgz", + "integrity": "sha512-hrky3DSgE0u7sQxaCL7bdebEPHx5QzYmrGuUjaPLmPE8jx5adtvGuOlRspvMoVLTTDOHRnZDoRLYJuA+VCI7Hg==", + "dev": true, + "dependencies": { + "@jest/core": "^27.4.5", + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "jest-config": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/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/jest-cli/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/jest-cli/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/jest-cli/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/jest-cli/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/jest-cli/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/jest-config": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.4.5.tgz", + "integrity": "sha512-t+STVJtPt+fpqQ8GBw850NtSQbnDOw/UzdPfzDaHQ48/AylQlW7LHj3dH+ndxhC1UxJ0Q3qkq7IH+nM1skwTwA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^27.4.5", + "@jest/types": "^27.4.2", + "babel-jest": "^27.4.5", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-circus": "^27.4.5", + "jest-environment-jsdom": "^27.4.4", + "jest-environment-node": "^27.4.4", + "jest-get-type": "^27.4.0", + "jest-jasmine2": "^27.4.5", + "jest-regex-util": "^27.4.0", + "jest-resolve": "^27.4.5", + "jest-runner": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "micromatch": "^4.0.4", + "pretty-format": "^27.4.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/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/jest-config/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/jest-config/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/jest-config/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/jest-config/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/jest-config/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/jest-diff": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.2.tgz", + "integrity": "sha512-ujc9ToyUZDh9KcqvQDkk/gkbf6zSaeEg9AiBxtttXW59H/AcqEYp1ciXAtJp+jXWva5nAf/ePtSsgWwE5mqp4Q==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.4.0", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-diff/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/jest-diff/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/jest-diff/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/jest-diff/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/jest-diff/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/jest-diff/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/jest-docblock": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz", + "integrity": "sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.4.2.tgz", + "integrity": "sha512-53V2MNyW28CTruB3lXaHNk6PkiIFuzdOC9gR3C6j8YE/ACfrPnz+slB0s17AgU1TtxNzLuHyvNlLJ+8QYw9nBg==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "chalk": "^4.0.0", + "jest-get-type": "^27.4.0", + "jest-util": "^27.4.2", + "pretty-format": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-each/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/jest-each/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/jest-each/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/jest-each/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/jest-each/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/jest-each/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/jest-environment-jsdom": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.4.tgz", + "integrity": "sha512-cYR3ndNfHBqQgFvS1RL7dNqSvD//K56j/q1s2ygNHcfTCAp12zfIromO1w3COmXrxS8hWAh7+CmZmGCIoqGcGA==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.4.4", + "@jest/fake-timers": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "jest-mock": "^27.4.2", + "jest-util": "^27.4.2", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.4.tgz", + "integrity": "sha512-D+v3lbJ2GjQTQR23TK0kY3vFVmSeea05giInI41HHOaJnAwOnmUHTZgUaZL+VxUB43pIzoa7PMwWtCVlIUoVoA==", + "dev": true, + "dependencies": { + "@jest/environment": "^27.4.4", + "@jest/fake-timers": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "jest-mock": "^27.4.2", + "jest-util": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", + "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.5.tgz", + "integrity": "sha512-oJm1b5qhhPs78K24EDGifWS0dELYxnoBiDhatT/FThgB9yxqUm5F6li3Pv+Q+apMBmmPNzOBnZ7ZxWMB1Leq1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^27.4.0", + "jest-serializer": "^27.4.0", + "jest-util": "^27.4.2", + "jest-worker": "^27.4.5", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.5.tgz", + "integrity": "sha512-oUnvwhJDj2LhOiUB1kdnJjkx8C5PwgUZQb9urF77mELH9DGR4e2GqpWQKBOYXWs5+uTN9BGDqRz3Aeg5Wts7aw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^27.4.4", + "@jest/source-map": "^27.4.0", + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.4.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.4.2", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-runtime": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "pretty-format": "^27.4.2", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/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/jest-jasmine2/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/jest-jasmine2/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/jest-jasmine2/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/jest-jasmine2/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/jest-jasmine2/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/jest-leak-detector": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.2.tgz", + "integrity": "sha512-ml0KvFYZllzPBJWDei3mDzUhyp/M4ubKebX++fPaudpe8OsxUE+m+P6ciVLboQsrzOCWDjE20/eXew9QMx/VGw==", + "dev": true, + "dependencies": { + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.2.tgz", + "integrity": "sha512-jyP28er3RRtMv+fmYC/PKG8wvAmfGcSNproVTW2Y0P/OY7/hWUOmsPfxN1jOhM+0u2xU984u2yEagGivz9OBGQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.4.2", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-message-util": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.2.tgz", + "integrity": "sha512-OMRqRNd9E0DkBLZpFtZkAGYOXl6ZpoMtQJWTAREJKDOFa0M6ptB7L67tp+cszMBkvSgKOhNtQp2Vbcz3ZZKo/w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.4.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-mock": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.2.tgz", + "integrity": "sha512-PDDPuyhoukk20JrQKeofK12hqtSka7mWH0QQuxSNgrdiPsrnYYLS6wbzu/HDlxZRzji5ylLRULeuI/vmZZDrYA==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz", + "integrity": "sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.5.tgz", + "integrity": "sha512-xU3z1BuOz/hUhVUL+918KqUgK+skqOuUsAi7A+iwoUldK6/+PW+utK8l8cxIWT9AW7IAhGNXjSAh1UYmjULZZw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.5.tgz", + "integrity": "sha512-elEVvkvRK51y037NshtEkEnukMBWvlPzZHiL847OrIljJ8yIsujD2GXRPqDXC4rEVKbcdsy7W0FxoZb4WmEs7w==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "jest-regex-util": "^27.4.0", + "jest-snapshot": "^27.4.5" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve/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/jest-resolve/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/jest-resolve/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/jest-resolve/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/jest-resolve/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/jest-resolve/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/jest-runner": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.5.tgz", + "integrity": "sha512-/irauncTfmY1WkTaRQGRWcyQLzK1g98GYG/8QvIPviHgO1Fqz1JYeEIsSfF+9mc/UTA6S+IIHFgKyvUrtiBIZg==", + "dev": true, + "dependencies": { + "@jest/console": "^27.4.2", + "@jest/environment": "^27.4.4", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-docblock": "^27.4.0", + "jest-environment-jsdom": "^27.4.4", + "jest-environment-node": "^27.4.4", + "jest-haste-map": "^27.4.5", + "jest-leak-detector": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-resolve": "^27.4.5", + "jest-runtime": "^27.4.5", + "jest-util": "^27.4.2", + "jest-worker": "^27.4.5", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runner/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/jest-runner/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/jest-runner/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/jest-runner/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/jest-runner/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/jest-runner/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/jest-runtime": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.5.tgz", + "integrity": "sha512-CIYqwuJQXHQtPd/idgrx4zgJ6iCb6uBjQq1RSAGQrw2S8XifDmoM1Ot8NRd80ooAm+ZNdHVwsktIMGlA1F1FAQ==", + "dev": true, + "dependencies": { + "@jest/console": "^27.4.2", + "@jest/environment": "^27.4.4", + "@jest/globals": "^27.4.4", + "@jest/source-map": "^27.4.0", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-message-util": "^27.4.2", + "jest-mock": "^27.4.2", + "jest-regex-util": "^27.4.0", + "jest-resolve": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^16.2.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-runtime/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/jest-runtime/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/jest-runtime/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/jest-runtime/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/jest-runtime/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/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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/jest-serializer": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz", + "integrity": "sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.5.tgz", + "integrity": "sha512-eCi/iM1YJFrJWiT9de4+RpWWWBqsHiYxFG9V9o/n0WXs6GpW4lUt4FAHAgFPTLPqCUVzrMQmSmTZSgQzwqR7IQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/parser": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.4.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^27.4.2", + "jest-get-type": "^27.4.0", + "jest-haste-map": "^27.4.5", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-resolve": "^27.4.5", + "jest-util": "^27.4.2", + "natural-compare": "^1.4.0", + "pretty-format": "^27.4.2", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot/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/jest-snapshot/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/jest-snapshot/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/jest-snapshot/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/jest-snapshot/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/jest-snapshot/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/jest-snapshot/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/jest-util": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz", + "integrity": "sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.4", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-util/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/jest-util/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/jest-util/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/jest-util/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/jest-util/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/jest-util/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/jest-validate": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.2.tgz", + "integrity": "sha512-hWYsSUej+Fs8ZhOm5vhWzwSLmVaPAxRy+Mr+z5MzeaHm9AxUpXdoVMEW4R86y5gOobVfBsMFLk4Rb+QkiEpx1A==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.4.0", + "leven": "^3.1.0", + "pretty-format": "^27.4.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/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/jest-validate/node_modules/camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/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/jest-validate/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/jest-validate/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/jest-validate/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/jest-validate/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/jest-watcher": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.2.tgz", + "integrity": "sha512-NJvMVyyBeXfDezhWzUOCOYZrUmkSCiatpjpm+nFUid74OZEHk6aMLrZAukIiFDwdbqp6mTM6Ui1w4oc+8EobQg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.4.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watcher/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/jest-watcher/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/jest-watcher/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/jest-watcher/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/jest-watcher/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/jest-watcher/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/jest-worker": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", + "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/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/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-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, + "peer": true + }, + "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, + "peer": true + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "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/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "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.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "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, + "peer": true + }, + "node_modules/logform": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", + "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", + "dependencies": { + "colors": "^1.2.1", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^1.1.0", + "triple-beam": "^1.3.0" + } + }, + "node_modules/logform/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==" + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "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/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "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=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/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=", + "engines": { + "node": ">= 0.6" + } + }, + "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/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "dependencies": { + "mime-db": "1.51.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/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/module-alias": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", + "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + }, + "node_modules/mongodb": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", + "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==", + "dependencies": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "optional-require": "^1.1.8", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4" + }, + "optionalDependencies": { + "saslprep": "^1.0.0" + }, + "peerDependenciesMeta": { + "aws4": { + "optional": true + }, + "bson-ext": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "mongodb-extjson": { + "optional": true + }, + "snappy": { + "optional": true + } + } + }, + "node_modules/mongodb/node_modules/bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "dependencies": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/multer": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", + "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "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-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, + "node_modules/nodemailer": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.2.tgz", + "integrity": "sha512-Dz7zVwlef4k5R71fdmxwR8Q39fiboGbu3xgswkzGwczUfjp873rVxt1O46+Fh0j1ORnAC6L9+heI8uUpO6DT7Q==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-run-path/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/nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, + "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=", + "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.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=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optional-require": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "dependencies": { + "require-at": "^1.0.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/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, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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=", + "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-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=" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pirates": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", + "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.2.tgz", + "integrity": "sha512-p0wNtJ9oLuvgOQDEIZ9zQjZffK7KtyR6Si0jnXULIDwrlNF8Cuir3AZP0hHv0jmKuNN/edOnbMjnzd4uTcmWiw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.4.2", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "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==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "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.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/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/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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dependencies": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/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/require-at": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-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==" + }, + "node_modules/safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "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.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/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": "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/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/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/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/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "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/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": "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/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-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-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, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/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/supports-hyperlinks/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/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true, + "peer": true + }, + "node_modules/throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-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/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/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/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "node_modules/ts-jest": { + "version": "27.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.2.tgz", + "integrity": "sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@types/jest": "^27.0.0", + "babel-jest": ">=27.0.0 <28", + "esbuild": "~0.14.0", + "jest": "^27.0.0", + "typescript": ">=3.8 <5.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/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/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + }, + "peerDependencies": { + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" + } + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "peerDependencies": { + "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + } + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "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==", + "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=" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "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/unique-names-generator": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.6.0.tgz", + "integrity": "sha512-m0fke1emBeT96UYn2psPQYwljooDWRTKt9oUZ5vlt88ZFMBGxqwPyLHXwCfkbgdm8jzioCp7oIpo6KdM+fnUlQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "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, + "peer": true + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz", + "integrity": "sha512-/PRhfd8aTNp9Ggr62HPzXg2XasNFGy5PBt0Rp04du7/8GNNSgxFL6WBTkgMKSL9bFjH+8kKEG3f37FmxiTqUUA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-encoding/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/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "dependencies": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-transport": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.1.tgz", + "integrity": "sha512-ciZRlU4CSjHqHe8RQG1iPxKMRVwv6ZJ0RC7DxStKWd0KjpAhPDy5gVYSCpIUq+5CUsP+IyNOTZy1X0tO2QZqjg==", + "dependencies": { + "logform": "^2.2.0", + "readable-stream": "^3.4.0", + "triple-beam": "^1.2.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", + "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", + "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/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.0" + } + }, + "@babel/compat-data": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", + "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", + "dev": true + }, + "@babel/core": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.5.tgz", + "integrity": "sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-compilation-targets": "^7.16.3", + "@babel/helper-module-transforms": "^7.16.5", + "@babel/helpers": "^7.16.5", + "@babel/parser": "^7.16.5", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.5", + "@babel/types": "^7.16.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.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 + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", + "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", + "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", + "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz", + "integrity": "sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.5", + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-simple-access": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.5", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz", + "integrity": "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", + "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helpers": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.5.tgz", + "integrity": "sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw==", + "dev": true, + "requires": { + "@babel/template": "^7.16.0", + "@babel/traverse": "^7.16.5", + "@babel/types": "^7.16.0" + } + }, + "@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.16.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", + "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.5.tgz", + "integrity": "sha512-/d4//lZ1Vqb4mZ5xTep3dDK888j7BGM/iKqBmndBaoYAFPlPKrGU608VVBz5JeyAb6YQDjRu1UKqj86UhwWVgw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.5" + } + }, + "@babel/template": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/traverse": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", + "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-environment-visitor": "^7.16.5", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.5", + "@babel/types": "^7.16.0", + "debug": "^4.1.0", + "globals": "^11.1.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 + } + } + }, + "@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "dev": true, + "peer": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "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, + "peer": true + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.2.tgz", + "integrity": "sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA==", + "dev": true, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.4.2.tgz", + "integrity": "sha512-xknHThRsPB/To1FUbi6pCe43y58qFC03zfb6R7fDb/FfC7k2R3i1l+izRBJf8DI46KhYGRaF14Eo9A3qbBoixg==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.4.2", + "jest-util": "^27.4.2", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + } + } + }, + "@jest/core": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.4.5.tgz", + "integrity": "sha512-3tm/Pevmi8bDsgvo73nX8p/WPng6KWlCyScW10FPEoN1HU4pwI83tJ3TsFvi1FfzsjwUlMNEPowgb/rPau/LTQ==", + "dev": true, + "requires": { + "@jest/console": "^27.4.2", + "@jest/reporters": "^27.4.5", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^27.4.2", + "jest-config": "^27.4.5", + "jest-haste-map": "^27.4.5", + "jest-message-util": "^27.4.2", + "jest-regex-util": "^27.4.0", + "jest-resolve": "^27.4.5", + "jest-resolve-dependencies": "^27.4.5", + "jest-runner": "^27.4.5", + "jest-runtime": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "jest-watcher": "^27.4.2", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.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" + } + } + } + }, + "@jest/environment": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.4.4.tgz", + "integrity": "sha512-q+niMx7cJgt/t/b6dzLOh4W8Ef/8VyKG7hxASK39jakijJzbFBGpptx3RXz13FFV7OishQ9lTbv+dQ5K3EhfDQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "jest-mock": "^27.4.2" + } + }, + "@jest/fake-timers": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.4.2.tgz", + "integrity": "sha512-f/Xpzn5YQk5adtqBgvw1V6bF8Nx3hY0OIRRpCvWcfPl0EAjdqWPdhH3t/3XpiWZqtjIEHDyMKP9ajpva1l4Zmg==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.4.2", + "jest-mock": "^27.4.2", + "jest-util": "^27.4.2" + } + }, + "@jest/globals": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.4.4.tgz", + "integrity": "sha512-bqpqQhW30BOreXM8bA8t8JbOQzsq/WnPTnBl+It3UxAD9J8yxEAaBEylHx1dtBapAr/UBk8GidXbzmqnee8tYQ==", + "dev": true, + "requires": { + "@jest/environment": "^27.4.4", + "@jest/types": "^27.4.2", + "expect": "^27.4.2" + } + }, + "@jest/reporters": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.4.5.tgz", + "integrity": "sha512-3orsG4vi8zXuBqEoy2LbnC1kuvkg1KQUgqNxmxpQgIOQEPeV0onvZu+qDQnEoX8qTQErtqn/xzcnbpeTuOLSiA==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.4.2", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^27.4.5", + "jest-resolve": "^27.4.5", + "jest-util": "^27.4.2", + "jest-worker": "^27.4.5", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.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" + } + } + } + }, + "@jest/source-map": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.4.0.tgz", + "integrity": "sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.4.2.tgz", + "integrity": "sha512-kr+bCrra9jfTgxHXHa2UwoQjxvQk3Am6QbpAiJ5x/50LW8llOYrxILkqY0lZRW/hu8FXesnudbql263+EW9iNA==", + "dev": true, + "requires": { + "@jest/console": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.4.5.tgz", + "integrity": "sha512-n5woIn/1v+FT+9hniymHPARA9upYUmfi5Pw9ewVwXCDlK4F5/Gkees9v8vdjGdAIJ2MPHLHodiajLpZZanWzEQ==", + "dev": true, + "requires": { + "@jest/test-result": "^27.4.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-runtime": "^27.4.5" + } + }, + "@jest/transform": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.4.5.tgz", + "integrity": "sha512-PuMet2UlZtlGzwc6L+aZmR3I7CEBpqadO03pU40l2RNY2fFJ191b9/ITB44LNOhVtsyykx0OZvj0PCyuLm7Eew==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.4.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-regex-util": "^27.4.0", + "jest-util": "^27.4.2", + "micromatch": "^4.0.4", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + } + } + }, + "@jest/types": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.4.2.tgz", + "integrity": "sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.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" + } + } + } + }, + "@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" + } + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.17", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.17.tgz", + "integrity": "sha512-6zzkezS9QEIL8yCBvXWxPTJPNuMeECJVxSOhxNY/jfq9LxOTHivaYTqr37n9LknWWRTIkzqH2UilS5QFvfa90A==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.3.tgz", + "integrity": "sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", + "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bson": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz", + "integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==", + "dev": true, + "requires": { + "bson": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.26", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz", + "integrity": "sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "27.0.3", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.0.3.tgz", + "integrity": "sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg==", + "dev": true, + "requires": { + "jest-diff": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "@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 + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "dev": true, + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "@types/morgan": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz", + "integrity": "sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/node": { + "version": "14.18.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.2.tgz", + "integrity": "sha512-fqtSN5xn/bBzDxMT77C1rJg6CsH/R49E7qsGuvdPJa20HtV5zSTuLJPNfnlyVH3wauKnkHdLggTVkOW/xP9oQg==", + "dev": true + }, + "@types/nodemailer": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.4.tgz", + "integrity": "sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/prettier": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.2.tgz", + "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/uuid": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", + "integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==", + "dev": true + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.1", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", + "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.8.1.tgz", + "integrity": "sha512-wTZ5oEKrKj/8/366qTM366zqhIKAp6NCMweoRONtfuC07OAU9nVI2GZZdqQ1qD30WAAtcPdkH+npDwtRFdp4Rw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "5.8.1", + "@typescript-eslint/scope-manager": "5.8.1", + "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" + } + }, + "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" + } + }, + "tsutils": { + "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" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.8.1.tgz", + "integrity": "sha512-fbodVnjIDU4JpeXWRDsG5IfIjYBxEvs8EBO8W1+YVdtrc2B9ppfof5sZhVEDOtgTfFHnYQJDI8+qdqLYO4ceww==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.8.1.tgz", + "integrity": "sha512-K1giKHAjHuyB421SoXMXFHHVI4NdNY603uKw92++D3qyxSeYvC10CBJ/GE5Thpo4WTUvu1mmJI2/FFkz38F2Gw==", + "dev": true, + "peer": true, + "requires": { + "@typescript-eslint/scope-manager": "5.8.1", + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/typescript-estree": "5.8.1", + "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, + "peer": 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, + "peer": true + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.8.1.tgz", + "integrity": "sha512-DGxJkNyYruFH3NIZc3PwrzwOQAg7vvgsHsHCILOLvUpupgkwDZdNq/cXU3BjF4LNrCsVg0qxEyWasys5AiJ85Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1" + } + }, + "@typescript-eslint/types": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.8.1.tgz", + "integrity": "sha512-L/FlWCCgnjKOLefdok90/pqInkomLnAcF9UAzNr+DSqMC3IffzumHTQTrINXhP1gVp9zlHiYYjvozVZDPleLcA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.8.1.tgz", + "integrity": "sha512-26lQ8l8tTbG7ri7xEcCFT9ijU5Fk+sx/KRRyyzCv7MQ+rZZlqiDPtMKWLC8P7o+dtCnby4c+OlxuX1tp8WfafQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.8.1", + "@typescript-eslint/visitor-keys": "5.8.1", + "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" + } + }, + "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" + } + }, + "tsutils": { + "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" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.8.1.tgz", + "integrity": "sha512-SWgiWIwocK6NralrJarPZlWdr0hZnj5GXHIgfdm8hNkyKvpeQuFyLP6YjSIe9kf3YBIfU6OHSZLYkQ+smZwtNg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.8.1", + "eslint-visitor-keys": "^3.0.0" + } + }, + "abab": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", + "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", + "dev": true + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "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, + "peer": true, + "requires": {} + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "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 + } + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": 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" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "peer": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "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": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "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 + }, + "async": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "babel-jest": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.5.tgz", + "integrity": "sha512-3uuUTjXbgtODmSv/DXO9nZfD52IyC2OYTFaXGRzL0kpykzroaquCrD5+lZNafTvZlnNqZHt5pb0M08qVBZnsnA==", + "dev": true, + "requires": { + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^27.4.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + } + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", + "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz", + "integrity": "sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz", + "integrity": "sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^27.4.0", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "bson": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.0.tgz", + "integrity": "sha512-8jw1NU1hglS+Da1jDOUYuNcBJ4cNHCFIqzlwoFNnsTOg2R/ox0aTYcTiBN4dzRa9q7Cvy6XErh3L8ReTEb9AQQ==", + "dev": true, + "requires": { + "buffer": "^5.6.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "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 + }, + "caniuse-lite": { + "version": "1.0.30001292", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", + "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==", + "dev": true + }, + "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" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "ci-info": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz", + "integrity": "sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "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==" + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "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" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", + "dev": true + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "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==" + }, + "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" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "denque": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", + "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.4.0.tgz", + "integrity": "sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==", + "dev": true + }, + "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" + }, + "dependencies": { + "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 + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.4.26", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.26.tgz", + "integrity": "sha512-cA1YwlRzO6TGp7yd3+KAqh9Tt6Z4CuuKqsAJP6uF/H5MQryjAGDhMhnY5cEXo8MaRCczpzSBhMPdqRIodkbZYw==", + "dev": true + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "peer": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "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" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "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 + }, + "escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.5.0.tgz", + "integrity": "sha512-tVGSkgNbOfiHyVte8bCM8OmX+xG9PzVG/B4UCF60zx7j61WIVY/AqJECDgpLD4DbbESD0e174gOg3ZlrX15GDg==", + "dev": true, + "peer": true, + "requires": { + "@eslint/eslintrc": "^1.0.5", + "@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", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.2.0", + "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": "^4.0.6", + "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", + "progress": "^2.0.0", + "regexpp": "^3.2.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "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, + "peer": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": 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, + "peer": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "peer": true, + "requires": { + "ms": "2.1.2" + } + }, + "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, + "peer": true + }, + "eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "peer": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "globals": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz", + "integrity": "sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "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, + "peer": 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==", + "dev": true, + "peer": 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, + "peer": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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, + "peer": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "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, + "peer": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "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, + "peer": true + }, + "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, + "peer": 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, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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, + "peer": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "peer": 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, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "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, + "peer": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "peer": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-plugin-jest": { + "version": "25.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.3.2.tgz", + "integrity": "sha512-1aPC7RRJkMCNgklHMDECw8fnzag3JBH53LaxmFkDTR7+PfMCO5V6f8XFRHoT2I+Fr4pVO9cPdRGlf7/haB2O5Q==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "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.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz", + "integrity": "sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA==", + "dev": true + }, + "espree": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.2.0.tgz", + "integrity": "sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg==", + "dev": true, + "peer": true, + "requires": { + "acorn": "^8.6.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "peer": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "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" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expect": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.4.2.tgz", + "integrity": "sha512-BjAXIDC6ZOW+WBFNg96J22D27Nq5ohn+oGcuP2rtOtcjuxNoV9McpQ60PcQWhdFOSBIQdR72e+4HdnbZTFSTyg==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "ansi-styles": "^5.0.0", + "jest-get-type": "^27.4.0", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-regex-util": "^27.4.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "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, + "peer": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "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": { + "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, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "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" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + }, + "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, + "peer": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "peer": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", + "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "dev": true, + "peer": true + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "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", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "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 + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "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" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "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" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "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 + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-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", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "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" + } + }, + "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-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "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 + } + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "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 + } + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "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.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "peer": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "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, + "peer": true + } + } + }, + "import-local": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz", + "integrity": "sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "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" + } + }, + "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==" + }, + "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-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-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-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "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" + } + }, + "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": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "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" + } + }, + "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": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "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.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" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "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" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "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 + } + } + }, + "istanbul-reports": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.1.tgz", + "integrity": "sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.4.5.tgz", + "integrity": "sha512-uT5MiVN3Jppt314kidCk47MYIRilJjA/l2mxwiuzzxGUeJIvA8/pDaJOAX5KWvjAo7SCydcW0/4WEtgbLMiJkg==", + "dev": true, + "requires": { + "@jest/core": "^27.4.5", + "import-local": "^3.0.2", + "jest-cli": "^27.4.5" + } + }, + "jest-changed-files": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.4.2.tgz", + "integrity": "sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.4.5.tgz", + "integrity": "sha512-eTNWa9wsvBwPykhMMShheafbwyakcdHZaEYh5iRrQ0PFJxkDP/e3U/FvzGuKWu2WpwUA3C3hPlfpuzvOdTVqnw==", + "dev": true, + "requires": { + "@jest/environment": "^27.4.4", + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.4.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.4.2", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-runtime": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "pretty-format": "^27.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "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" + } + } + } + }, + "jest-cli": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.4.5.tgz", + "integrity": "sha512-hrky3DSgE0u7sQxaCL7bdebEPHx5QzYmrGuUjaPLmPE8jx5adtvGuOlRspvMoVLTTDOHRnZDoRLYJuA+VCI7Hg==", + "dev": true, + "requires": { + "@jest/core": "^27.4.5", + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "jest-config": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "prompts": "^2.0.1", + "yargs": "^16.2.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" + } + } + } + }, + "jest-config": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.4.5.tgz", + "integrity": "sha512-t+STVJtPt+fpqQ8GBw850NtSQbnDOw/UzdPfzDaHQ48/AylQlW7LHj3dH+ndxhC1UxJ0Q3qkq7IH+nM1skwTwA==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^27.4.5", + "@jest/types": "^27.4.2", + "babel-jest": "^27.4.5", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-circus": "^27.4.5", + "jest-environment-jsdom": "^27.4.4", + "jest-environment-node": "^27.4.4", + "jest-get-type": "^27.4.0", + "jest-jasmine2": "^27.4.5", + "jest-regex-util": "^27.4.0", + "jest-resolve": "^27.4.5", + "jest-runner": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "micromatch": "^4.0.4", + "pretty-format": "^27.4.2", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + } + } + }, + "jest-diff": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.4.2.tgz", + "integrity": "sha512-ujc9ToyUZDh9KcqvQDkk/gkbf6zSaeEg9AiBxtttXW59H/AcqEYp1ciXAtJp+jXWva5nAf/ePtSsgWwE5mqp4Q==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.4.0", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.2" + }, + "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" + } + } + } + }, + "jest-docblock": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.4.0.tgz", + "integrity": "sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.4.2.tgz", + "integrity": "sha512-53V2MNyW28CTruB3lXaHNk6PkiIFuzdOC9gR3C6j8YE/ACfrPnz+slB0s17AgU1TtxNzLuHyvNlLJ+8QYw9nBg==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "chalk": "^4.0.0", + "jest-get-type": "^27.4.0", + "jest-util": "^27.4.2", + "pretty-format": "^27.4.2" + }, + "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" + } + } + } + }, + "jest-environment-jsdom": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.4.4.tgz", + "integrity": "sha512-cYR3ndNfHBqQgFvS1RL7dNqSvD//K56j/q1s2ygNHcfTCAp12zfIromO1w3COmXrxS8hWAh7+CmZmGCIoqGcGA==", + "dev": true, + "requires": { + "@jest/environment": "^27.4.4", + "@jest/fake-timers": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "jest-mock": "^27.4.2", + "jest-util": "^27.4.2", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.4.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.4.4.tgz", + "integrity": "sha512-D+v3lbJ2GjQTQR23TK0kY3vFVmSeea05giInI41HHOaJnAwOnmUHTZgUaZL+VxUB43pIzoa7PMwWtCVlIUoVoA==", + "dev": true, + "requires": { + "@jest/environment": "^27.4.4", + "@jest/fake-timers": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "jest-mock": "^27.4.2", + "jest-util": "^27.4.2" + } + }, + "jest-get-type": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.4.0.tgz", + "integrity": "sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==", + "dev": true + }, + "jest-haste-map": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.4.5.tgz", + "integrity": "sha512-oJm1b5qhhPs78K24EDGifWS0dELYxnoBiDhatT/FThgB9yxqUm5F6li3Pv+Q+apMBmmPNzOBnZ7ZxWMB1Leq1Q==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^27.4.0", + "jest-serializer": "^27.4.0", + "jest-util": "^27.4.2", + "jest-worker": "^27.4.5", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.4.5.tgz", + "integrity": "sha512-oUnvwhJDj2LhOiUB1kdnJjkx8C5PwgUZQb9urF77mELH9DGR4e2GqpWQKBOYXWs5+uTN9BGDqRz3Aeg5Wts7aw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^27.4.4", + "@jest/source-map": "^27.4.0", + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.4.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.4.2", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-runtime": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "pretty-format": "^27.4.2", + "throat": "^6.0.1" + }, + "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" + } + } + } + }, + "jest-leak-detector": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.4.2.tgz", + "integrity": "sha512-ml0KvFYZllzPBJWDei3mDzUhyp/M4ubKebX++fPaudpe8OsxUE+m+P6ciVLboQsrzOCWDjE20/eXew9QMx/VGw==", + "dev": true, + "requires": { + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.2" + } + }, + "jest-matcher-utils": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.4.2.tgz", + "integrity": "sha512-jyP28er3RRtMv+fmYC/PKG8wvAmfGcSNproVTW2Y0P/OY7/hWUOmsPfxN1jOhM+0u2xU984u2yEagGivz9OBGQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.4.2", + "jest-get-type": "^27.4.0", + "pretty-format": "^27.4.2" + }, + "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" + } + } + } + }, + "jest-message-util": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.4.2.tgz", + "integrity": "sha512-OMRqRNd9E0DkBLZpFtZkAGYOXl6ZpoMtQJWTAREJKDOFa0M6ptB7L67tp+cszMBkvSgKOhNtQp2Vbcz3ZZKo/w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.4.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.4", + "pretty-format": "^27.4.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "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" + } + } + } + }, + "jest-mock": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.4.2.tgz", + "integrity": "sha512-PDDPuyhoukk20JrQKeofK12hqtSka7mWH0QQuxSNgrdiPsrnYYLS6wbzu/HDlxZRzji5ylLRULeuI/vmZZDrYA==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.4.0.tgz", + "integrity": "sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg==", + "dev": true + }, + "jest-resolve": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.4.5.tgz", + "integrity": "sha512-xU3z1BuOz/hUhVUL+918KqUgK+skqOuUsAi7A+iwoUldK6/+PW+utK8l8cxIWT9AW7IAhGNXjSAh1UYmjULZZw==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.5.tgz", + "integrity": "sha512-elEVvkvRK51y037NshtEkEnukMBWvlPzZHiL847OrIljJ8yIsujD2GXRPqDXC4rEVKbcdsy7W0FxoZb4WmEs7w==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "jest-regex-util": "^27.4.0", + "jest-snapshot": "^27.4.5" + } + }, + "jest-runner": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.4.5.tgz", + "integrity": "sha512-/irauncTfmY1WkTaRQGRWcyQLzK1g98GYG/8QvIPviHgO1Fqz1JYeEIsSfF+9mc/UTA6S+IIHFgKyvUrtiBIZg==", + "dev": true, + "requires": { + "@jest/console": "^27.4.2", + "@jest/environment": "^27.4.4", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-docblock": "^27.4.0", + "jest-environment-jsdom": "^27.4.4", + "jest-environment-node": "^27.4.4", + "jest-haste-map": "^27.4.5", + "jest-leak-detector": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-resolve": "^27.4.5", + "jest-runtime": "^27.4.5", + "jest-util": "^27.4.2", + "jest-worker": "^27.4.5", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "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" + } + } + } + }, + "jest-runtime": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.4.5.tgz", + "integrity": "sha512-CIYqwuJQXHQtPd/idgrx4zgJ6iCb6uBjQq1RSAGQrw2S8XifDmoM1Ot8NRd80ooAm+ZNdHVwsktIMGlA1F1FAQ==", + "dev": true, + "requires": { + "@jest/console": "^27.4.2", + "@jest/environment": "^27.4.4", + "@jest/globals": "^27.4.4", + "@jest/source-map": "^27.4.0", + "@jest/test-result": "^27.4.2", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^27.4.5", + "jest-message-util": "^27.4.2", + "jest-mock": "^27.4.2", + "jest-regex-util": "^27.4.0", + "jest-resolve": "^27.4.5", + "jest-snapshot": "^27.4.5", + "jest-util": "^27.4.2", + "jest-validate": "^27.4.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^16.2.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 + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "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" + } + } + } + }, + "jest-serializer": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.4.0.tgz", + "integrity": "sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ==", + "dev": true, + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + } + }, + "jest-snapshot": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.4.5.tgz", + "integrity": "sha512-eCi/iM1YJFrJWiT9de4+RpWWWBqsHiYxFG9V9o/n0WXs6GpW4lUt4FAHAgFPTLPqCUVzrMQmSmTZSgQzwqR7IQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/parser": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.4.5", + "@jest/types": "^27.4.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.4.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^27.4.2", + "jest-get-type": "^27.4.0", + "jest-haste-map": "^27.4.5", + "jest-matcher-utils": "^27.4.2", + "jest-message-util": "^27.4.2", + "jest-resolve": "^27.4.5", + "jest-util": "^27.4.2", + "natural-compare": "^1.4.0", + "pretty-format": "^27.4.2", + "semver": "^7.3.2" + }, + "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 + }, + "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": "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" + } + } + } + }, + "jest-util": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.4.2.tgz", + "integrity": "sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.4", + "picomatch": "^2.2.3" + }, + "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" + } + } + } + }, + "jest-validate": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.4.2.tgz", + "integrity": "sha512-hWYsSUej+Fs8ZhOm5vhWzwSLmVaPAxRy+Mr+z5MzeaHm9AxUpXdoVMEW4R86y5gOobVfBsMFLk4Rb+QkiEpx1A==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.4.0", + "leven": "^3.1.0", + "pretty-format": "^27.4.2" + }, + "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" + } + }, + "camelcase": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", + "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "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, + "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" + } + } + } + }, + "jest-watcher": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.4.2.tgz", + "integrity": "sha512-NJvMVyyBeXfDezhWzUOCOYZrUmkSCiatpjpm+nFUid74OZEHk6aMLrZAukIiFDwdbqp6mTM6Ui1w4oc+8EobQg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "@jest/test-result": "^27.4.2", + "@jest/types": "^27.4.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.4.2", + "string-length": "^4.0.1" + }, + "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" + } + } } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "jest-worker": { + "version": "27.4.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.5.tgz", + "integrity": "sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -866,21 +14224,85 @@ "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-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, + "peer": true + }, + "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, + "peer": true + }, + "json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -890,11 +14312,33 @@ "universalify": "^2.0.0" } }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "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=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -907,29 +14351,98 @@ "strip-bom": "^3.0.0" } }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "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, + "peer": true + }, "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.0.tgz", + "integrity": "sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==", "requires": { "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", "fecha": "^4.2.0", "ms": "^2.1.1", + "safe-stable-stringify": "^1.1.0", "triple-beam": "^1.3.0" }, "dependencies": { "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "loglevel": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", - "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==" + }, + "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" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } }, "media-typer": { "version": "0.3.0", @@ -953,29 +14466,57 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "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=" }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", "requires": { - "mime-db": "1.44.0" + "mime-db": "1.51.0" } }, + "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 + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1003,16 +14544,23 @@ "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" }, "mongodb": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz", - "integrity": "sha512-WlirMiuV1UPbej5JeCMqE93JRfZ/ZzqE7nJTwP85XzjAF4rRSeq2bGCb1cjfoHLOF06+HxADaPGqT0g3SbVT1w==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz", + "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==", "requires": { "bl": "^2.2.1", "bson": "^1.1.4", "denque": "^1.4.1", - "optional-require": "^1.0.2", + "optional-require": "^1.1.8", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" + }, + "dependencies": { + "bson": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz", + "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==" + } } }, "morgan": { @@ -1033,20 +14581,26 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "multer": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", - "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", + "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", "requires": { "append-field": "^1.0.0", "busboy": "^0.2.11", "concat-stream": "^1.5.2", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.4", "object-assign": "^4.1.1", "on-finished": "^2.3.0", "type-is": "^1.6.4", "xtend": "^4.0.0" } }, + "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 + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -1058,10 +14612,22 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true + }, "nodemailer": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.0.tgz", - "integrity": "sha512-ikSMDU1nZqpo2WUPE0wTTw/NGGImTkwpJKDIFPZT+YvvR9Sj+ze5wzu95JHkBMglQLoG2ITxU21WukCC/XsFkg==" + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.2.tgz", + "integrity": "sha512-Dz7zVwlef4k5R71fdmxwR8Q39fiboGbu3xgswkzGwczUfjp873rVxt1O46+Fh0j1ORnAC6L9+heI8uUpO6DT7Q==" }, "normalize-package-data": { "version": "2.5.0", @@ -1075,6 +14641,12 @@ "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-all": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", @@ -1092,15 +14664,38 @@ "string.prototype.padend": "^3.0.0" } }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + }, + "dependencies": { + "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 + } + } + }, + "nwsapi": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "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": { @@ -1110,15 +14705,15 @@ "dev": true }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "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": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, "on-finished": { @@ -1150,10 +14745,70 @@ "fn.name": "1.x.x" } }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "optional-require": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz", - "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "requires": { + "require-at": "^1.0.6" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.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 + }, + "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, + "peer": true, + "requires": { + "callsites": "^3.0.0" + } }, "parse-json": { "version": "4.0.0", @@ -1165,11 +14820,23 @@ "json-parse-better-errors": "^1.0.1" } }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -1201,6 +14868,18 @@ "pify": "^3.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 + }, + "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 + }, "pidtree": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", @@ -1213,25 +14892,101 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, + "pirates": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz", + "integrity": "sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.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 + }, + "pretty-format": { + "version": "27.4.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.4.2.tgz", + "integrity": "sha512-p0wNtJ9oLuvgOQDEIZ9zQjZffK7KtyR6Si0jnXULIDwrlNF8Cuir3AZP0hHv0jmKuNN/edOnbMjnzd4uTcmWiw==", + "dev": true, + "requires": { + "@jest/types": "^27.4.2", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "peer": true + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "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==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "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.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "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 + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1248,6 +15003,12 @@ "unpipe": "1.0.0" } }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -1273,13 +15034,76 @@ "util-deprecate": "~1.0.1" } }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-at": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==" + }, + "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 + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": 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 + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-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": { - "path-parse": "^1.0.6" + "queue-microtask": "^1.2.2" } }, "safe-buffer": { @@ -1287,6 +15111,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", + "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1301,6 +15130,15 @@ "sparse-bitfield": "^3.0.3" } }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -1359,9 +15197,26 @@ "dev": true }, "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "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.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "dev": true }, "simple-swizzle": { @@ -1379,6 +15234,34 @@ } } }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "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.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -1415,9 +15298,9 @@ } }, "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==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", "dev": true }, "sprintf-js": { @@ -1431,6 +15314,23 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "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 + } + } + }, "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -1441,42 +15341,73 @@ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.padend": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz", - "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "es-abstract": "^1.19.1" } }, "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "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": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "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": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, - "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==", + "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, "requires": { - "safe-buffer": "~5.1.0" + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -1485,6 +15416,19 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-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, + "peer": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1494,20 +15438,163 @@ "has-flag": "^3.0.0" } }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true, + "peer": true + }, + "throat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", + "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "dev": true + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "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 + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + }, + "dependencies": { + "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 + } + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "ts-jest": { + "version": "27.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.2.tgz", + "integrity": "sha512-eSOiJOWq6Hhs6Khzk5wKC5sgWIXgXqOCiIl1+3lfnearu58Hj4QpE5tUhQcA3xtZrELbcvAGCsd6HB8OsaVaTA==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^27.0.0", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "20.x" + }, + "dependencies": { + "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" + } + } + } + }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, "tslint": { @@ -1540,6 +15627,27 @@ "tslib": "^1.8.1" } }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1554,16 +15662,37 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typescript": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", - "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", "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" + } + }, "unique-names-generator": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.5.0.tgz", - "integrity": "sha512-GaiWvo3rKIHi1SyYP8/9cDrPMPSwEiYDUo2p0NQHeCHDXzLn8P6p8bttSS3lX7HHS3Yl5vnw/ulybO4GN85CgQ==" + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.6.0.tgz", + "integrity": "sha512-m0fke1emBeT96UYn2psPQYwljooDWRTKt9oUZ5vlt88ZFMBGxqwPyLHXwCfkbgdm8jzioCp7oIpo6KdM+fnUlQ==" }, "universalify": { "version": "2.0.0", @@ -1575,6 +15704,16 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "requires": { + "punycode": "^2.1.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1590,6 +15729,32 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, + "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, + "peer": true + }, + "v8-to-istanbul": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz", + "integrity": "sha512-/PRhfd8aTNp9Ggr62HPzXg2XasNFGy5PBt0Rp04du7/8GNNSgxFL6WBTkgMKSL9bFjH+8kKEG3f37FmxiTqUUA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "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", @@ -1605,6 +15770,76 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -1614,6 +15849,19 @@ "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" + } + }, "winston": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", @@ -1643,12 +15891,68 @@ } }, "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.1.tgz", + "integrity": "sha512-ciZRlU4CSjHqHe8RQG1iPxKMRVwv6ZJ0RC7DxStKWd0KjpAhPDy5gVYSCpIUq+5CUsP+IyNOTZy1X0tO2QZqjg==", "requires": { - "readable-stream": "^2.3.7", + "logform": "^2.2.0", + "readable-stream": "^3.4.0", "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "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 + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } } }, "wrappy": { @@ -1656,10 +15960,74 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.6.tgz", + "integrity": "sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==", + "dev": true, + "requires": {} + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "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": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true } } } diff --git a/package.json b/package.json index 83d6da50..2d1153e4 100755 --- a/package.json +++ b/package.json @@ -1,78 +1,81 @@ -{ - "name": "iamus-metaverse-server", - "version": "2.4.10", - "private": true, - "description": "metaverse-server for Vircadia", - "author": "Robert Adams ", - "repository": { - "type": "git", - "url": "https://github.com/vircadia/Iamus" - }, - "bugs": { - "url": "https://github.com/vircadia/Iamus/issues" - }, - "homepage": "https://github.com/vircadia/Iamus", - "keywords": [ - "Vircadia", - "virtual world" - ], - "license": "Apache-2.0", - "main": "dist/index.js", - "_moduleAliases": { - "@Entities": "dist/Entities", - "@Monitoring": "dist/Monitoring", - "@Route-Tools": "dist/route-tools", - "@Tools": "dist/Tools", - "@Base": "dist" - }, - "scripts": { - "run": "node dist/index.js", - "build": "npm-run-all -s \"clean-dist\" \"create-version\" \"copy-static\" \"build:iamus\"", - "prebuild:iamus": "tslint -c tslint.json -p tsconfig.json --fix", - "build:iamus": "tsc", - "prestart": "npm run build", - "start": "node dist/index.js", - "clean-dist": "node build_scripts/cleanDist.js", - "create-version": "node build_scripts/createVersion.js", - "copy-static": "node build_scripts/copyStatic.js", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "engines": { - "node": ">= 14.18.1", - "npm": ">= 6.14.15" - }, - "dependencies": { - "cors": "^2.8.5", - "debug": "~2.6.9", - "deepmerge": "^4.2.2", - "express": "~4.16.1", - "fs-extra": "^9.1.0", - "glob": "^7.1.7", - "http-errors": "~1.6.3", - "loglevel": "^1.7.1", - "module-alias": "^2.2.2", - "mongodb": "^3.6.6", - "morgan": "~1.9.1", - "multer": "^1.4.2", - "nodemailer": "^6.6.0", - "unique-names-generator": "^4.5.0", - "uuid": "^8.3.2", - "winston": "^3.3.3" - }, - "devDependencies": { - "@types/cors": "^2.8.10", - "@types/debug": "^4.1.5", - "@types/express": "^4.17.11", - "@types/glob": "^7.1.3", - "@types/http-errors": "^1.8.0", - "@types/mongodb": "^3.6.12", - "@types/morgan": "^1.9.2", - "@types/multer": "^1.4.5", - "@types/node": "^14.14.44", - "@types/nodemailer": "^6.4.1", - "@types/uuid": "^8.3.0", - "npm-run-all": "^4.1.5", - "tslint": "^6.1.3", - "typescript": "^3.9.9" - } -} +{ + "name": "iamus-metaverse-server", + "version": "2.4.10", + "private": true, + "description": "metaverse-server for Vircadia", + "author": "Robert Adams ", + "repository": { + "type": "git", + "url": "https://github.com/vircadia/Iamus" + }, + "bugs": { + "url": "https://github.com/vircadia/Iamus/issues" + }, + "homepage": "https://github.com/vircadia/Iamus", + "keywords": [ + "Vircadia", + "virtual world" + ], + "license": "Apache-2.0", + "main": "dist/index.js", + "_moduleAliases": { + "@Entities": "dist/Entities", + "@Monitoring": "dist/Monitoring", + "@Route-Tools": "dist/route-tools", + "@Tools": "dist/Tools", + "@Base": "dist" + }, + "scripts": { + "run": "node dist/index.js", + "build": "npm-run-all -s \"clean-dist\" \"create-version\" \"copy-static\" \"build:iamus\"", + "build:iamus": "tsc", + "prestart": "npm run build", + "start": "node dist/index.js", + "clean-dist": "node build_scripts/cleanDist.js", + "create-version": "node build_scripts/createVersion.js", + "copy-static": "node build_scripts/copyStatic.js", + "test": "jest" + }, + "engines": { + "node": ">= 14.18.1", + "npm": ">= 6.14.15" + }, + "dependencies": { + "cors": "^2.8.5", + "debug": "~2.6.9", + "deepmerge": "^4.2.2", + "express": "~4.16.1", + "fs-extra": "^9.1.0", + "glob": "^7.1.7", + "http-errors": "~1.6.3", + "loglevel": "^1.7.1", + "module-alias": "^2.2.2", + "mongodb": "^3.6.6", + "morgan": "~1.9.1", + "multer": "^1.4.2", + "nodemailer": "^6.6.0", + "unique-names-generator": "^4.5.0", + "uuid": "^8.3.2", + "winston": "^3.3.3" + }, + "devDependencies": { + "@types/cors": "^2.8.10", + "@types/debug": "^4.1.5", + "@types/express": "^4.17.11", + "@types/glob": "^7.1.3", + "@types/http-errors": "^1.8.0", + "@types/jest": "^27.0.3", + "@types/mongodb": "^3.6.12", + "@types/morgan": "^1.9.2", + "@types/multer": "^1.4.5", + "@types/node": "^14.14.44", + "@types/nodemailer": "^6.4.1", + "@types/uuid": "^8.3.0", + "@typescript-eslint/eslint-plugin": "^5.8.1", + "eslint-plugin-jest": "^25.3.2", + "jest": "^27.4.5", + "npm-run-all": "^4.1.5", + "ts-jest": "^27.1.2", + "typescript": "^3.9.9" + } +} diff --git a/src/Entities/AccountEntity.ts b/src/Entities/AccountEntity.ts index a24baf44..ac920cc2 100755 --- a/src/Entities/AccountEntity.ts +++ b/src/Entities/AccountEntity.ts @@ -11,9 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Entity } from '@Entities/Entity'; + +import { Entity } from "@Entities/Entity"; // NOTE: this class cannot have functions in them as they are just JSON to and from the database export class AccountEntity implements Entity { @@ -56,4 +56,4 @@ export class AccountEntity implements Entity { public IPAddrOfCreator: string; // IP address that created this account public whenCreated: Date; // date of account creation public timeOfLastHeartbeat: Date; // when we last heard from this user -}; \ No newline at end of file +} diff --git a/src/Entities/AccountFields.ts b/src/Entities/AccountFields.ts index a14b44b4..830064da 100644 --- a/src/Entities/AccountFields.ts +++ b/src/Entities/AccountFields.ts @@ -11,86 +11,82 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Entity } from '@Entities/Entity'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { Accounts } from '@Entities/Accounts'; -import { Roles } from '@Entities/Sets/Roles'; -import { Availability } from '@Entities/Sets/Availability'; +import { Entity } from "@Entities/Entity"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { Accounts } from "@Entities/Accounts"; -import { isStringValidator, isBooleanValidator, isPathValidator, isNumberValidator } from '@Route-Tools/Validators'; -import { isObjectValidator, isSArraySet, isDateValidator } from '@Route-Tools/Validators'; -import { simpleGetter, simpleSetter, sArraySetter, dateStringGetter } from '@Route-Tools/Validators'; -import { noGetter, noSetter, verifyAllSArraySetValues } from '@Route-Tools/Validators'; -import { Perm } from '@Route-Tools/Perm'; -import { FieldDefn, ValidateResponse } from '@Route-Tools/EntityFieldDefn'; +import { Roles } from "@Entities/Sets/Roles"; +import { Availability } from "@Entities/Sets/Availability"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { IsNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; -import Config from '@Base/config'; +import { isStringValidator, isBooleanValidator, isPathValidator, isNumberValidator } from "@Route-Tools/Validators"; +import { isObjectValidator, isSArraySet, isDateValidator } from "@Route-Tools/Validators"; +import { simpleGetter, simpleSetter, sArraySetter, dateStringGetter } from "@Route-Tools/Validators"; +import { noGetter, noSetter, verifyAllSArraySetValues } from "@Route-Tools/Validators"; +import { Perm } from "@Route-Tools/Perm"; +import { FieldDefn, ValidateResponse } from "@Route-Tools/EntityFieldDefn"; + +import { VKeyedCollection } from "@Tools/vTypes"; +import { IsNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; +import Config from "@Base/config"; export function CheckAccountFields(): void { // DEBUG DEBUG: for unknown reasons some field ops end up 'undefined' - Object.keys(accountFields).forEach( fieldName => { + Object.keys(accountFields).forEach((fieldName) => { const defn = accountFields[fieldName]; - if (typeof(defn.validate) !== 'function') { + if (typeof defn.validate !== "function") { Logger.error(`CheckAccountFields: field ${defn.entity_field} validator is not a function`); - }; - if (typeof(defn.getter) !== 'function') { + } + if (typeof defn.getter !== "function") { Logger.error(`CheckAccountFields: field ${defn.entity_field} getter is not a function`); - }; - if (typeof(defn.setter) !== 'function') { + } + if (typeof defn.setter !== "function") { Logger.error(`CheckAccountFields: field ${defn.entity_field} setter is not a function`); - }; + } }); // END DEBUG DEBUG -}; +} // Naming and access for the fields in a AccountEntity. // Indexed by request_field_name. export const accountFields: { [key: string]: FieldDefn } = { - 'id': { - entity_field: 'id', - request_field_name: 'id', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "id": { + entity_field: "id", + request_field_name: "id", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'username': { - entity_field: 'username', - request_field_name: 'username', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "username": { + entity_field: "username", + request_field_name: "username", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { let validity: ValidateResponse; - if (typeof(pVal) === 'string' && pVal.length > 0) { - if (pVal.length <= Config['metaverse-server']['max-name-length']) { - if (/^[A-Za-z][A-Za-z0-9+\-_\.]*$/.test(pVal)) { + if (typeof pVal === "string" && pVal.length > 0) { + if (pVal.length <= Config["metaverse-server"]["max-name-length"]) { + if ((/^[A-Za-z][A-Za-z0-9+\-_\.]*$/).test(pVal)) { // Make sure no other account has this username const otherAccount = await Accounts.getAccountWithUsername(pVal); if (IsNullOrEmpty(otherAccount) || otherAccount.id === (pEntity as AccountEntity).id) { validity = { valid: true }; + } else { + validity = { valid: false, reason: "username already exists" }; } - else { - validity = { valid: false, reason: 'username already exists' }; - }; + } else { + validity = { valid: false, reason: "username can contain only A-Za-z0-9+-_." }; } - else { - validity = { valid: false, reason: 'username can contain only A-Za-z0-9+-_.' }; - }; + } else { + validity = { valid: false, reason: "username too long" }; } - else { - validity = { valid: false, reason: 'username too long' }; - }; + } else { + validity = { valid: false, reason: "username must be a simple string" }; } - else { - validity = { valid: false, reason: 'username must be a simple string' }; - }; return validity; }, // Changing the username requires searching the database and changing the name @@ -103,14 +99,14 @@ export const accountFields: { [key: string]: FieldDefn } = { }, getter: simpleGetter }, - 'email': { - entity_field: 'email', - request_field_name: 'email', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "email": { + entity_field: "email", + request_field_name: "email", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { let validity: ValidateResponse; - if (typeof(pVal) === 'string' && pVal.length > 0) { + if (typeof pVal === "string" && pVal.length > 0) { // Check email for sanity // Old style check which doesn't cover all the RFC complient email addresses possible // if (/^[A-Za-z0-9+\-_\.]+@[A-Za-z0-9-\.]+$/.test(pVal)) { @@ -120,18 +116,15 @@ export const accountFields: { [key: string]: FieldDefn } = { const otherAccount = await Accounts.getAccountWithEmail(pVal); if (IsNullOrEmpty(otherAccount) || otherAccount.id === (pEntity as AccountEntity).id) { validity = { valid: true }; + } else { + validity = { valid: false, reason: "email already exists for another account" }; } - else { - validity = { valid: false, reason: 'email already exists for another account' }; - }; + } else { + validity = { valid: false, reason: "email address needs one AT sign" }; } - else { - validity = { valid: false, reason: 'email address needs one AT sign' }; - }; + } else { + validity = { valid: false, reason: "email must be a simple string" }; } - else { - validity = { valid: false, reason: 'email must be a simple string' }; - }; return validity; }, setter: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { @@ -140,252 +133,252 @@ export const accountFields: { [key: string]: FieldDefn } = { }, getter: simpleGetter }, - 'account_settings': { - entity_field: 'accountSettings', - request_field_name: 'account_settings', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "account_settings": { + entity_field: "accountSettings", + request_field_name: "account_settings", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'images_hero': { - entity_field: 'imagesHero', - request_field_name: 'images_hero', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "images_hero": { + entity_field: "imagesHero", + request_field_name: "images_hero", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'images_tiny': { - entity_field: 'imagesTiny', - request_field_name: 'images_tiny', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "images_tiny": { + entity_field: "imagesTiny", + request_field_name: "images_tiny", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'images_thumbnail': { - entity_field: 'imagesThumbnail', - request_field_name: 'images_thumbnail', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "images_thumbnail": { + entity_field: "imagesThumbnail", + request_field_name: "images_thumbnail", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'connected': { - entity_field: 'locationConnected', - request_field_name: 'connected', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "connected": { + entity_field: "locationConnected", + request_field_name: "connected", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isBooleanValidator, setter: simpleSetter, getter: simpleGetter }, - 'path': { - entity_field: 'locationPath', - request_field_name: 'path', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "path": { + entity_field: "locationPath", + request_field_name: "path", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isPathValidator, setter: simpleSetter, getter: simpleGetter }, - 'place_id': { - entity_field: 'locationPlaceId', - request_field_name: 'place_id', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "place_id": { + entity_field: "locationPlaceId", + request_field_name: "place_id", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'domain_id': { - entity_field: 'locationDomainId', - request_field_name: 'domain_id', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "domain_id": { + entity_field: "locationDomainId", + request_field_name: "domain_id", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'network_address': { - entity_field: 'locationNetworkAddress', - request_field_name: 'network_address', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "network_address": { + entity_field: "locationNetworkAddress", + request_field_name: "network_address", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'network_port': { - entity_field: 'locationNetworkPort', - request_field_name: 'network_port', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "network_port": { + entity_field: "locationNetworkPort", + request_field_name: "network_port", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isNumberValidator, setter: simpleSetter, getter: simpleGetter }, - 'node_id': { - entity_field: 'locationNodeId', - request_field_name: 'node_id', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "node_id": { + entity_field: "locationNodeId", + request_field_name: "node_id", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'availability': { - entity_field: 'availability', - request_field_name: 'availability', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "availability": { + entity_field: "availability", + request_field_name: "availability", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { if (await verifyAllSArraySetValues(pVal, Availability.KnownAvailability)) { return { valid: true }; } - return { valid: false, reason: 'not legal availability value'}; + return { valid: false, reason: "not legal availability value" }; }, setter: sArraySetter, getter: simpleGetter }, - 'connections': { - entity_field: 'connections', - request_field_name: 'connections', - get_permissions: [ Perm.OWNER, Perm.ADMIN, 'friend', 'connection' ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "connections": { + entity_field: "connections", + request_field_name: "connections", + get_permissions: [Perm.OWNER, Perm.ADMIN, "friend", "connection"], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isSArraySet, setter: sArraySetter, getter: simpleGetter }, - 'friends': { - entity_field: 'friends', - request_field_name: 'friends', - get_permissions: [ Perm.OWNER, Perm.ADMIN, 'friend' ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "friends": { + entity_field: "friends", + request_field_name: "friends", + get_permissions: [Perm.OWNER, Perm.ADMIN, "friend"], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isSArraySet, setter: sArraySetter, getter: simpleGetter }, - 'locker': { - entity_field: 'locker', - request_field_name: 'locker', - get_permissions: [ Perm.OWNER, Perm.ADMIN ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "locker": { + entity_field: "locker", + request_field_name: "locker", + get_permissions: [Perm.OWNER, Perm.ADMIN], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isObjectValidator, setter: simpleSetter, getter: simpleGetter }, - 'profile_detail': { - entity_field: 'profileDetail', - request_field_name: 'profile_detail', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "profile_detail": { + entity_field: "profileDetail", + request_field_name: "profile_detail", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isObjectValidator, setter: simpleSetter, getter: simpleGetter }, // User authentication - 'password': { - entity_field: 'password', - request_field_name: 'password', - get_permissions: [ Perm.NONE ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "password": { + entity_field: "password", + request_field_name: "password", + get_permissions: [Perm.NONE], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - Accounts.storePassword((pEntity as AccountEntity), pVal); + Accounts.storePassword(pEntity as AccountEntity, pVal); }, getter: noGetter, - // An update to the password means updates to hash and salt fields. - updater: (pField: FieldDefn, pEntity: Entity, pUpdates: VKeyedCollection): void => { + // An update to the password means updates to hash and salt fields. + updater: (pField: FieldDefn, pEntity: Entity, pUpdates: VKeyedCollection): void => { pUpdates.passwordHash = (pEntity as AccountEntity).passwordHash; pUpdates.passwordSalt = (pEntity as AccountEntity).passwordSalt; } }, - 'public_key': { - entity_field: 'sessionPublicKey', - request_field_name: 'public_key', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "public_key": { + entity_field: "sessionPublicKey", + request_field_name: "public_key", + get_permissions: [Perm.ALL], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, // Old stuff - 'xmpp_password': { - entity_field: 'xmppPassword', - request_field_name: 'xmpp_password', - get_permissions: [ Perm.OWNER ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "xmpp_password": { + entity_field: "xmppPassword", + request_field_name: "xmpp_password", + get_permissions: [Perm.OWNER], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'discourse_api_key': { - entity_field: 'discourseApiKey', - request_field_name: 'discourse_api_key', - get_permissions: [ Perm.OWNER ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "discourse_api_key": { + entity_field: "discourseApiKey", + request_field_name: "discourse_api_key", + get_permissions: [Perm.OWNER], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'wallet_id': { - entity_field: 'walletId', - request_field_name: 'wallet_id', - get_permissions: [ Perm.OWNER ], - set_permissions: [ Perm.OWNER, Perm.ADMIN ], + "wallet_id": { + entity_field: "walletId", + request_field_name: "wallet_id", + get_permissions: [Perm.OWNER], + set_permissions: [Perm.OWNER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, // Admin stuff - 'roles': { - entity_field: 'roles', - request_field_name: 'roles', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.ADMIN ], + "roles": { + entity_field: "roles", + request_field_name: "roles", + get_permissions: [Perm.ALL], + set_permissions: [Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { if (await verifyAllSArraySetValues(pVal, Roles.KnownRole)) { return { valid: true }; } - return { valid: false, reason: 'not valid role name'}; + return { valid: false, reason: "not valid role name" }; }, setter: sArraySetter, getter: simpleGetter }, - 'ip_addr_of_creator': { - entity_field: 'IPAddrOfCreator', - request_field_name: 'ip_addr_of_creator', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "ip_addr_of_creator": { + entity_field: "IPAddrOfCreator", + request_field_name: "ip_addr_of_creator", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: noSetter, getter: simpleGetter }, - 'when_account_created': { - entity_field: 'whenCreated', - request_field_name: 'when_account_created', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "when_account_created": { + entity_field: "whenCreated", + request_field_name: "when_account_created", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isDateValidator, setter: noSetter, getter: dateStringGetter }, - 'time_of_last_heartbeat': { - entity_field: 'timeOfLastHeartbeat', - request_field_name: 'time_of_last_heartbeat', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.ADMIN ], + "time_of_last_heartbeat": { + entity_field: "timeOfLastHeartbeat", + request_field_name: "time_of_last_heartbeat", + get_permissions: [Perm.ALL], + set_permissions: [Perm.ADMIN], validate: isDateValidator, setter: noSetter, getter: dateStringGetter - }, + } }; diff --git a/src/Entities/Accounts.ts b/src/Entities/Accounts.ts index c2ec4bac..c6a81156 100755 --- a/src/Entities/Accounts.ts +++ b/src/Entities/Accounts.ts @@ -11,58 +11,59 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import crypto from 'crypto'; +import { Config } from "@Base/config"; -import { AccountEntity } from '@Entities/AccountEntity'; -import { accountFields, CheckAccountFields } from '@Entities/AccountFields'; +import crypto from "crypto"; -import { Domains } from '@Entities/Domains'; -import { Roles } from '@Entities/Sets/Roles'; -import { Places } from '@Entities/Places'; -import { Tokens } from '@Entities/Tokens'; -import { AuthToken } from '@Entities/AuthToken'; +import { AccountEntity } from "@Entities/AccountEntity"; +import { accountFields, CheckAccountFields } from "@Entities/AccountFields"; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { Domains } from "@Entities/Domains"; +import { Roles } from "@Entities/Sets/Roles"; +import { Places } from "@Entities/Places"; +import { Tokens } from "@Entities/Tokens"; +import { AuthToken } from "@Entities/AuthToken"; -import { ValidateResponse } from '@Route-Tools/EntityFieldDefn'; -import { getEntityField, setEntityField, getEntityUpdateForField } from '@Route-Tools/GetterSetter'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; -import { createObject, getObject, getObjects, updateObjectFields, deleteOne, noCaseCollation, countObjects } from '@Tools/Db'; -import { GenUUID, genRandomString, IsNullOrEmpty, IsNotNullOrEmpty, SendVerificationEmail } from '@Tools/Misc'; -import { VKeyedCollection, SArray } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; -import { Requests } from './Requests'; +import { ValidateResponse } from "@Route-Tools/EntityFieldDefn"; +import { getEntityField, setEntityField, getEntityUpdateForField } from "@Route-Tools/GetterSetter"; -export let accountCollection = 'accounts'; +import { createObject, getObject, getObjects, updateObjectFields, deleteOne, noCaseCollation, countObjects } from "@Tools/Db"; +import { GenUUID, genRandomString, IsNullOrEmpty, IsNotNullOrEmpty, SendVerificationEmail } from "@Tools/Misc"; +import { VKeyedCollection, SArray } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; +import { Requests } from "./Requests"; + +export const accountCollection = "accounts"; // Initialize account management. export function initAccounts(): void { // Validate the fields have been initialized CheckAccountFields(); -}; +} export const Accounts = { async getAccountWithId(pAccountId: string): Promise { - return IsNullOrEmpty(pAccountId) ? null : getObject(accountCollection, - new GenericFilter({ 'id': pAccountId })); + return IsNullOrEmpty(pAccountId) + ? null + : getObject(accountCollection, + new GenericFilter({ "id": pAccountId })); }, async getAccountWithAuthToken(pAuthToken: string): Promise { if (IsNotNullOrEmpty(pAuthToken)) { try { const tokenInfo = await Tokens.getTokenWithToken(pAuthToken); if (IsNotNullOrEmpty(tokenInfo)) { - return Accounts.getAccountWithId(tokenInfo.accountId); - }; - } - catch (err) { + return await Accounts.getAccountWithId(tokenInfo.accountId); + } + } catch (err) { throw err; - }; - }; + } + } return null; }, async getAccountWithUsername(pUsername: string): Promise { @@ -71,28 +72,32 @@ export const Accounts = { // When indexes are added, create a 'username' case-insensitive index. // Need to clean the username of search characters since we're just passing it to the database return getObject(accountCollection, - new GenericFilter({ 'username': pUsername }), noCaseCollation ); - // { 'username': new RegExp(['^', pUsername.replace('[\\\*]', ''), '$'].join(''), 'i') } ); - }; + new GenericFilter({ "username": pUsername }), noCaseCollation); + // { 'username': new RegExp(['^', pUsername.replace('[\\\*]', ''), '$'].join(''), 'i') } ); + } return null; }, async getAccountWithNodeId(pNodeId: string): Promise { - return IsNullOrEmpty(pNodeId) ? null : getObject(accountCollection, - new GenericFilter({ 'locationNodeid': pNodeId })); + return IsNullOrEmpty(pNodeId) + ? null + : getObject(accountCollection, + new GenericFilter({ "locationNodeid": pNodeId })); }, async getAccountWithEmail(email: string): Promise { - return IsNullOrEmpty(email) ? null : getObject(accountCollection, - new GenericFilter({ 'email': email }), noCaseCollation ); + return IsNullOrEmpty(email) + ? null + : getObject(accountCollection, + new GenericFilter({ email }), noCaseCollation); }, - async addAccount(pAccountEntity: AccountEntity) : Promise { + async addAccount(pAccountEntity: AccountEntity): Promise { Logger.info(`Accounts: creating account ${pAccountEntity.username}, id=${pAccountEntity.id}`); return createObject(accountCollection, pAccountEntity); }, - async removeAccount(pAccountEntity: AccountEntity) : Promise { + async removeAccount(pAccountEntity: AccountEntity): Promise { Logger.info(`Accounts: removing account ${pAccountEntity.username}, id=${pAccountEntity.id}`); - return deleteOne(accountCollection, new GenericFilter({ 'id': pAccountEntity.id }) ); + return deleteOne(accountCollection, new GenericFilter({ "id": pAccountEntity.id })); }, - async removeAccountContext(pAccountEntity: AccountEntity) : Promise { + async removeAccountContext(pAccountEntity: AccountEntity): Promise { // Friends and Connections Logger.info(`Accounts: removing relationships for account ${pAccountEntity.username}, id=${pAccountEntity.id}`); if (pAccountEntity.connections) { @@ -100,46 +105,46 @@ export const Accounts = { const aConnection = await Accounts.getAccountWithUsername(aConnectionName); if (aConnection && aConnection.connections) { SArray.remove(aConnection.connections, pAccountEntity.username); - }; - }; - }; + } + } + } if (pAccountEntity.friends) { for (const aFriendName of pAccountEntity.friends) { const aFriend = await Accounts.getAccountWithUsername(aFriendName); if (aFriend && aFriend.friends) { SArray.remove(aFriend.friends, pAccountEntity.username); - }; - }; - }; + } + } + } // The domains associated with this account are removed also - for await (const aDomain of Domains.enumerateAsync(new GenericFilter({ 'sponsorAccountId': pAccountEntity.id }))) { + for await (const aDomain of Domains.enumerateAsync(new GenericFilter({ "sponsorAccountId": pAccountEntity.id }))) { await Domains.removeDomain(aDomain); await Domains.removeDomainContext(aDomain); - }; + } // Also, any places - await Places.removeMany(new GenericFilter( { 'accountId': pAccountEntity.id })); + await Places.removeMany(new GenericFilter({ "accountId": pAccountEntity.id })); }, // The contents of this entity have been updated async updateEntityFields(pEntity: AccountEntity, pFields: VKeyedCollection): Promise { - return updateObjectFields(accountCollection, new GenericFilter({ 'id': pEntity.id }), pFields); + return updateObjectFields(accountCollection, new GenericFilter({ "id": pEntity.id }), pFields); }, // Get the value of a domain field with the fieldname. // Checks to make sure the getter has permission to get the values. // Returns the value. Could be 'undefined' whether the requestor doesn't have permissions or that's // the actual field value. async getField(pAuthToken: AuthToken, pAccount: AccountEntity, - pField: string, pRequestingAccount?: AccountEntity): Promise { + pField: string, pRequestingAccount?: AccountEntity): Promise { return getEntityField(accountFields, pAuthToken, pAccount, pField, pRequestingAccount); }, // Set a domain field with the fieldname and a value. // Checks to make sure the setter has permission to set. // Returns 'true' if the value was set and 'false' if the value could not be set. async setField(pAuthToken: AuthToken, // authorization for making this change - pAccount: AccountEntity, // the account being changed - pField: string, pVal: any, // field being changed and the new value - pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known - pUpdates?: VKeyedCollection // where to record updates made (optional) - ): Promise { + pAccount: AccountEntity, // the account being changed + pField: string, pVal: any, // field being changed and the new value + pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known + pUpdates?: VKeyedCollection // where to record updates made (optional) + ): Promise { return setEntityField(accountFields, pAuthToken, pAccount, pField, pVal, pRequestingAccount, pUpdates); }, // Generate an 'update' block for the specified field or fields. @@ -148,16 +153,16 @@ export const Accounts = { // we want the actual value (whatever it is) to go into the database. // If an existing VKeyedCollection is passed, it is added to an returned. getUpdateForField(pAccount: AccountEntity, - pField: string | string[], pExisting?: VKeyedCollection): VKeyedCollection { + pField: string | string[], pExisting?: VKeyedCollection): VKeyedCollection { return getEntityUpdateForField(accountFields, pAccount, pField, pExisting); }, // Verify that the passed value is legal for the named field async validateFieldValue(pFieldName: string, pValue: any): Promise { const defn = accountFields[pFieldName]; if (defn) { - return await defn.validate(defn, defn.request_field_name, pValue); - }; - return { 'valid': false, 'reason': 'Unknown field name' }; + return defn.validate(defn, defn.request_field_name, pValue); + } + return { "valid": false, "reason": "Unknown field name" }; }, // Return the number of accounts that match the criteria async accountCount(pCriteria: CriteriaFilter): Promise { @@ -165,12 +170,12 @@ export const Accounts = { }, createAccount(pUsername: string, pPassword: string, pEmail: string): AccountEntity { const newAcct = new AccountEntity(); - newAcct.id= GenUUID(); + newAcct.id = GenUUID(); newAcct.username = pUsername; newAcct.email = pEmail.toLowerCase(); newAcct.roles = [Roles.USER]; - newAcct.friends = [] - newAcct.connections = [] + newAcct.friends = []; + newAcct.connections = []; newAcct.whenCreated = new Date(); // Remember the password @@ -183,48 +188,46 @@ export const Accounts = { // Returns true if account is active now. False if waiting for some verification enableAccount(pAccount: AccountEntity): boolean { let accountIsActive = false; - if (Config['metaverse-server']['enable-account-email-verification']) { + if (Config["metaverse-server"]["enable-account-email-verification"]) { pAccount.accountEmailVerified = false; const verifyCode = GenUUID(); // Create a pending request and wait for the user to check back const request = Requests.createEmailVerificationRequest(pAccount.id, verifyCode); void Requests.add(request); void SendVerificationEmail(pAccount, verifyCode); - } - else { + } else { // If not doing email verification, just turn on the account Accounts.doEnableAccount(pAccount); accountIsActive = true; - }; + } return accountIsActive; }, // The 'enableAccount' function can start a process of account enablement. // This function is called to actually turn on the account doEnableAccount(pAccount: AccountEntity): void { - if (Config['metaverse-server']['enable-account-email-verification']) { + if (Config["metaverse-server"]["enable-account-email-verification"]) { pAccount.accountEmailVerified = true; const updates: VKeyedCollection = { - 'accountEmailVerified': true + "accountEmailVerified": true }; void Accounts.updateEntityFields(pAccount, updates); - } - else { + } else { // If accounts are not verified by email, this value does not exist on the // account and thus enablement is assumed to be 'true'. delete pAccount.accountEmailVerified; const updates: VKeyedCollection = { - 'accountEmailVerified': null + "accountEmailVerified": null }; void Accounts.updateEntityFields(pAccount, updates); - }; + } }, // TODO: add scope (admin) and filter criteria filtering // It's push down to this routine so we could possibly use DB magic for the queries async *enumerateAsync(pPager: CriteriaFilter, - pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { + pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { for await (const acct of getObjects(accountCollection, pPager, pInfoer, pScoper)) { yield acct; - }; + } // return getObjects(accountCollection, pCriteria, pPager); }, @@ -236,9 +239,9 @@ export const Accounts = { return Accounts.hashPassword(pPassword, pAcct.passwordSalt) === pAcct.passwordHash; }, hashPassword(pPassword: string, pSalt: string): string { - const hash = crypto.createHmac('sha512', pSalt); + const hash = crypto.createHmac("sha512", pSalt); hash.update(pPassword); - const val = hash.digest('hex'); + const val = hash.digest("hex"); return val; }, // Create whatever datastructure is needed to make these accounts friends @@ -247,7 +250,7 @@ export const Accounts = { if (SArray.hasNoCase(pRequestingAccount.connections, pTargetAccount.username)) { const adminToken = Tokens.createSpecialAdminToken(); const updates: VKeyedCollection = {}; - await Accounts.setField(adminToken, pRequestingAccount, 'friends', { 'add': pTargetAccount.username }, pRequestingAccount, updates); + await Accounts.setField(adminToken, pRequestingAccount, "friends", { "add": pTargetAccount.username }, pRequestingAccount, updates); await Accounts.updateEntityFields(pRequestingAccount, updates); } }, @@ -255,52 +258,52 @@ export const Accounts = { async makeAccountsConnected(pRequestingAccount: AccountEntity, pTargetAccount: AccountEntity): Promise { const adminToken = Tokens.createSpecialAdminToken(); let updates: VKeyedCollection = {}; - await Accounts.setField(adminToken, pRequestingAccount, 'connections', { 'add': pTargetAccount.username }, pRequestingAccount, updates); + await Accounts.setField(adminToken, pRequestingAccount, "connections", { "add": pTargetAccount.username }, pRequestingAccount, updates); await Accounts.updateEntityFields(pRequestingAccount, updates); updates = {}; - await Accounts.setField(adminToken, pTargetAccount, 'connections', { 'add': pRequestingAccount.username }, pTargetAccount, updates); + await Accounts.setField(adminToken, pTargetAccount, "connections", { "add": pRequestingAccount.username }, pTargetAccount, updates); await Accounts.updateEntityFields(pTargetAccount, updates); }, // Remove the named account from the list of connections. Also cleans out the other side async removeConnection(pAccount: AccountEntity, pConnectionName: string) { let updates: VKeyedCollection = {}; const adminToken = Tokens.createSpecialAdminToken(); - await Accounts.setField(adminToken, pAccount, 'connections', { 'remove': pConnectionName }, pAccount, updates); - await Accounts.setField(adminToken, pAccount, 'friends', { 'remove': pConnectionName }, pAccount, updates); + await Accounts.setField(adminToken, pAccount, "connections", { "remove": pConnectionName }, pAccount, updates); + await Accounts.setField(adminToken, pAccount, "friends", { "remove": pConnectionName }, pAccount, updates); await Accounts.updateEntityFields(pAccount, updates); const otherAccount = await Accounts.getAccountWithUsername(pConnectionName); if (otherAccount) { - updates = {}; - await Accounts.setField(adminToken, otherAccount, 'connections', { 'remove': pAccount.username }, otherAccount, updates); - await Accounts.setField(adminToken, otherAccount, 'friends', { 'remove': pAccount.username }, otherAccount, updates); + updates = {}; + await Accounts.setField(adminToken, otherAccount, "connections", { "remove": pAccount.username }, otherAccount, updates); + await Accounts.setField(adminToken, otherAccount, "friends", { "remove": pAccount.username }, otherAccount, updates); await Accounts.updateEntityFields(otherAccount, updates); - }; + } }, async removeFriend(pAccount: AccountEntity, pFriendName: string) { let updates: VKeyedCollection = {}; const adminToken = Tokens.createSpecialAdminToken(); - await Accounts.setField(adminToken, pAccount, 'friends', { 'remove': pFriendName }, pAccount, updates); + await Accounts.setField(adminToken, pAccount, "friends", { "remove": pFriendName }, pAccount, updates); await Accounts.updateEntityFields(pAccount, updates); const otherAccount = await Accounts.getAccountWithUsername(pFriendName); if (otherAccount) { updates = {}; - await Accounts.setField(adminToken, otherAccount, 'friends', { 'remove': pAccount.username }, otherAccount, updates); + await Accounts.setField(adminToken, otherAccount, "friends", { "remove": pAccount.username }, otherAccount, updates); await Accounts.updateEntityFields(otherAccount, updates); - }; + } }, // getter property that is 'true' if the user has been heard from recently isOnline(pAcct: AccountEntity): boolean { if (pAcct && pAcct.timeOfLastHeartbeat) { - return (Date.now().valueOf() - pAcct.timeOfLastHeartbeat.valueOf()) - < (Config["metaverse-server"]["heartbeat-seconds-until-offline"] * 1000); - }; + return Date.now().valueOf() - pAcct.timeOfLastHeartbeat.valueOf() + < Config["metaverse-server"]["heartbeat-seconds-until-offline"] * 1000; + } return false; }, // Return the ISODate when an account is considered offline dateWhenNotOnline(): Date { const whenOffline = new Date( Date.now() - - (Config["metaverse-server"]["heartbeat-seconds-until-offline"] * 1000) + - Config["metaverse-server"]["heartbeat-seconds-until-offline"] * 1000 ); return whenOffline; }, diff --git a/src/Entities/AuthToken.ts b/src/Entities/AuthToken.ts index 7123ed58..64a1fd82 100755 --- a/src/Entities/AuthToken.ts +++ b/src/Entities/AuthToken.ts @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' + // NOTE: these cannot have instance functions in them as they are just JSON to and from the database export class AuthToken { @@ -22,4 +22,4 @@ export class AuthToken { public accountId: string; // AccountId of associated account public whenCreated: Date; public expirationTime: Date; -}; +} diff --git a/src/Entities/DomainEntity.ts b/src/Entities/DomainEntity.ts index 61f64345..3ee2c4b5 100755 --- a/src/Entities/DomainEntity.ts +++ b/src/Entities/DomainEntity.ts @@ -11,11 +11,11 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; -import { Entity } from '@Entities/Entity'; +import Config from "@Base/config"; + +import { Entity } from "@Entities/Entity"; // NOTE: this class cannot have functions in them as they are just JSON to and from the database export class DomainEntity implements Entity { @@ -56,5 +56,4 @@ export class DomainEntity implements Entity { public timeOfLastHeartbeat: Date; // time of last heartbeat public lastSenderKey: string; // a key identifying the sender -}; - +} diff --git a/src/Entities/DomainFields.ts b/src/Entities/DomainFields.ts index 17be2f57..b73f9d9a 100644 --- a/src/Entities/DomainFields.ts +++ b/src/Entities/DomainFields.ts @@ -11,353 +11,348 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; -import { Entity } from '@Entities/Entity'; -import { Accounts } from '@Entities/Accounts'; +import Config from "@Base/config"; -import { Maturity } from '@Entities/Sets/Maturity'; -import { Visibility } from '@Entities/Sets/Visibility'; -import { Restriction } from '@Entities/Sets/Restriction'; +import { Entity } from "@Entities/Entity"; +import { Accounts } from "@Entities/Accounts"; -import { Perm } from '@Route-Tools/Perm'; +import { Maturity } from "@Entities/Sets/Maturity"; +import { Visibility } from "@Entities/Sets/Visibility"; +import { Restriction } from "@Entities/Sets/Restriction"; -import { FieldDefn, ValidateResponse } from '@Route-Tools/EntityFieldDefn'; -import { isStringValidator, isNumberValidator, isBooleanValidator, isSArraySet, isDateValidator } from '@Route-Tools/Validators'; -import { simpleGetter, simpleSetter, noGetter, noSetter, noOverwriteSetter, sArraySetter, dateStringGetter } from '@Route-Tools/Validators'; -import { verifyAllSArraySetValues} from '@Route-Tools/Validators'; +import { Perm } from "@Route-Tools/Perm"; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; +import { FieldDefn, ValidateResponse } from "@Route-Tools/EntityFieldDefn"; +import { isStringValidator, isNumberValidator, isBooleanValidator, isSArraySet, isDateValidator } from "@Route-Tools/Validators"; +import { simpleGetter, simpleSetter, noGetter, noSetter, noOverwriteSetter, sArraySetter, dateStringGetter } from "@Route-Tools/Validators"; +import { verifyAllSArraySetValues } from "@Route-Tools/Validators"; -import { Logger } from '@Tools/Logging'; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; + +import { Logger } from "@Tools/Logging"; export function CheckDomainFields(): void { // DEBUG DEBUG: for unknown reasons some field ops end up 'undefined' - Object.keys(DomainFields).forEach( fieldName => { + Object.keys(DomainFields).forEach((fieldName) => { const defn = DomainFields[fieldName]; - if (typeof(defn.validate) !== 'function') { + if (typeof defn.validate !== "function") { Logger.error(`CheckDomainFields: field ${defn.entity_field} validator is not a function`); - }; - if (typeof(defn.getter) !== 'function') { + } + if (typeof defn.getter !== "function") { Logger.error(`CheckDomainFields: field ${defn.entity_field} getter is not a function`); - }; - if (typeof(defn.setter) !== 'function') { + } + if (typeof defn.setter !== "function") { Logger.error(`CheckDomainFields: field ${defn.entity_field} setter is not a function`); - }; + } }); // END DEBUG DEBUG -}; +} // Naming and access for the fields in a DomainEntity. // Indexed by request_field_name. export const DomainFields: { [key: string]: FieldDefn } = { - 'id': { - entity_field: 'id', - request_field_name: 'id', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "id": { + entity_field: "id", + request_field_name: "id", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'name': { - entity_field: 'name', - request_field_name: 'name', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "name": { + entity_field: "name", + request_field_name: "name", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { let validity: ValidateResponse; - if (typeof(pVal) === 'string' && pVal.length > 0) { - if (pVal.length <= Config['metaverse-server']['max-name-length']) { - if( /^[A-Za-z][A-Za-z0-9+\-_\.]*$/.test(pVal) ) { + if (typeof pVal === "string" && pVal.length > 0) { + if (pVal.length <= Config["metaverse-server"]["max-name-length"]) { + if ((/^[A-Za-z][A-Za-z0-9+\-_\.]*$/).test(pVal)) { validity = { valid: true }; + } else { + validity = { valid: false, reason: "domain name characters must be A-Za-z0-9+-_." }; } - else { - validity = { valid: false, reason: 'domain name characters must be A-Za-z0-9+-_.'}; - }; + } else { + validity = { valid: false, reason: "domain name too long" }; } - else { - validity = { valid: false, reason: 'domain name too long'}; - }; + } else { + validity = { valid: false, reason: "domain name must be a simple string" }; } - else { - validity = { valid: false, reason: 'domain name must be a simple string'}; - }; return validity; }, setter: noOverwriteSetter, getter: simpleGetter }, - 'visiblity': { - entity_field: 'visiblity', - request_field_name: 'visiblity', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "visiblity": { + entity_field: "visiblity", + request_field_name: "visiblity", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - if(typeof(pVal) === 'string' && Visibility.KnownVisibility(pVal)) { + if (typeof pVal === "string" && Visibility.KnownVisibility(pVal)) { return { valid: true }; } - return { valid: false, reason: 'not accepted visibility value'}; + return { valid: false, reason: "not accepted visibility value" }; }, setter: simpleSetter, getter: simpleGetter }, // An alternate way of setting domain name - 'world_name': { - entity_field: 'name', - request_field_name: 'world_name', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "world_name": { + entity_field: "name", + request_field_name: "world_name", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { let validity: ValidateResponse; - if (typeof(pVal) === 'string' && pVal.length > 0) { - if (/^[A-Za-z][A-Za-z0-9+\-_\.]*$/.test(pVal)) { + if (typeof pVal === "string" && pVal.length > 0) { + if ((/^[A-Za-z][A-Za-z0-9+\-_\.]*$/).test(pVal)) { validity = { valid: true }; + } else { + validity = { valid: false, reason: "domain name characters must be A-Za-z0-9+-_." }; } - else { - validity = { valid: false, reason: 'domain name characters must be A-Za-z0-9+-_.'}; - }; + } else { + validity = { valid: false, reason: "domain name must be a simple string" }; } - else { - validity = { valid: false, reason: 'domain name must be a simple string'}; - }; return validity; }, setter: noOverwriteSetter, getter: simpleGetter }, - 'public_key': { - entity_field: 'publicKey', - request_field_name: 'public_key', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN ], + "public_key": { + entity_field: "publicKey", + request_field_name: "public_key", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'sponsor_account_id': { - entity_field: 'sponsorAccountId', - request_field_name: 'sponsor_account_id', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "sponsor_account_id": { + entity_field: "sponsorAccountId", + request_field_name: "sponsor_account_id", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'version': { - entity_field: 'version', - request_field_name: 'version', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN ], + "version": { + entity_field: "version", + request_field_name: "version", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'protocol': { - entity_field: 'protocol', - request_field_name: 'protocol', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN ], + "protocol": { + entity_field: "protocol", + request_field_name: "protocol", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'network_address': { - entity_field: 'networkAddr', - request_field_name: 'network_address', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN ], + "network_address": { + entity_field: "networkAddr", + request_field_name: "network_address", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'network_port': { - entity_field: 'networkPort', - request_field_name: 'network_port', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN ], + "network_port": { + entity_field: "networkPort", + request_field_name: "network_port", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN], validate: isNumberValidator, setter: simpleSetter, getter: simpleGetter }, - 'automatic_networking': { - entity_field: 'networkingMode', - request_field_name: 'automatic_networking', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN ], + "automatic_networking": { + entity_field: "networkingMode", + request_field_name: "automatic_networking", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'num_users': { - entity_field: 'numUsers', - request_field_name: 'num_users', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.ADMIN ], + "num_users": { + entity_field: "numUsers", + request_field_name: "num_users", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.ADMIN], validate: isNumberValidator, setter: simpleSetter, getter: simpleGetter }, - 'num_anon_users': { - entity_field: 'anonUsers', - request_field_name: 'num_anon_users', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.ADMIN ], + "num_anon_users": { + entity_field: "anonUsers", + request_field_name: "num_anon_users", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.ADMIN], validate: isNumberValidator, setter: simpleSetter, getter: simpleGetter }, - 'restricted': { - entity_field: 'restricted', - request_field_name: 'restricted', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "restricted": { + entity_field: "restricted", + request_field_name: "restricted", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isBooleanValidator, setter: simpleSetter, getter: simpleGetter }, - 'capacity': { - entity_field: 'capacity', - request_field_name: 'capacity', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "capacity": { + entity_field: "capacity", + request_field_name: "capacity", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isNumberValidator, setter: simpleSetter, getter: simpleGetter }, - 'description': { - entity_field: 'description', - request_field_name: 'description', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "description": { + entity_field: "description", + request_field_name: "description", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isStringValidator, setter: noOverwriteSetter, getter: simpleGetter }, - 'contact_info': { - entity_field: 'contactInfo', - request_field_name: 'contact_info', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "contact_info": { + entity_field: "contactInfo", + request_field_name: "contact_info", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isStringValidator, setter: noOverwriteSetter, getter: simpleGetter }, - 'thumbnail': { - entity_field: 'thumbnail', - request_field_name: 'thumbnail', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "thumbnail": { + entity_field: "thumbnail", + request_field_name: "thumbnail", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isStringValidator, setter: noOverwriteSetter, getter: simpleGetter }, - 'images': { - entity_field: 'images', - request_field_name: 'images', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "images": { + entity_field: "images", + request_field_name: "images", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isSArraySet, setter: sArraySetter, getter: simpleGetter }, - 'maturity': { - entity_field: 'maturity', - request_field_name: 'maturity', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "maturity": { + entity_field: "maturity", + request_field_name: "maturity", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - if(typeof(pVal) === 'string' && Maturity.KnownMaturity(pVal)) { + if (typeof pVal === "string" && Maturity.KnownMaturity(pVal)) { return { valid: true }; } - return { valid: false, reason: 'not accepted maturity value'}; + return { valid: false, reason: "not accepted maturity value" }; }, setter: simpleSetter, getter: simpleGetter }, - 'restriction': { - entity_field: 'restriction', - request_field_name: 'restriction', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "restriction": { + entity_field: "restriction", + request_field_name: "restriction", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - if(typeof(pVal) === 'string' && Restriction.KnownRestriction(pVal)) { + if (typeof pVal === "string" && Restriction.KnownRestriction(pVal)) { return { valid: true }; } - return { valid: false, reason: 'not accepted restriction value'}; + return { valid: false, reason: "not accepted restriction value" }; }, setter: simpleSetter, getter: simpleGetter }, - 'managers': { - entity_field: 'managers', - request_field_name: 'managers', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.SPONSOR, Perm.ADMIN ], + "managers": { + entity_field: "managers", + request_field_name: "managers", + get_permissions: [Perm.ALL], + set_permissions: [Perm.SPONSOR, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - if (await verifyAllSArraySetValues(pVal, async ( name: string ) => { + if (await verifyAllSArraySetValues(pVal, async (name: string) => { return IsNotNullOrEmpty(await Accounts.getAccountWithUsername(name)); - } ) ) { + })) { return { valid: true }; } - return { valid: false, reason: 'unknown user names'}; + return { valid: false, reason: "unknown user names" }; }, setter: sArraySetter, getter: simpleGetter }, - 'tags': { - entity_field: 'tags', - request_field_name: 'tags', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ], + "tags": { + entity_field: "tags", + request_field_name: "tags", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN], validate: isSArraySet, setter: sArraySetter, getter: simpleGetter }, // admin stuff - 'addr_of_first_contact': { - entity_field: 'iPAddrOfFirstContact', - request_field_name: 'addr_of_first_contact', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "addr_of_first_contact": { + entity_field: "iPAddrOfFirstContact", + request_field_name: "addr_of_first_contact", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'when_domain_entry_created': { - entity_field: 'whenCreated', - request_field_name: 'when_domain_entry_created', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "when_domain_entry_created": { + entity_field: "whenCreated", + request_field_name: "when_domain_entry_created", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isDateValidator, setter: noSetter, getter: dateStringGetter }, - 'active': { - entity_field: 'active', - request_field_name: 'active', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "active": { + entity_field: "active", + request_field_name: "active", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isBooleanValidator, setter: noSetter, getter: simpleGetter }, - 'time_of_last_heartbeat': { - entity_field: 'timeOfLastHeartbeat', - request_field_name: 'time_of_last_heartbeat', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "time_of_last_heartbeat": { + entity_field: "timeOfLastHeartbeat", + request_field_name: "time_of_last_heartbeat", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isDateValidator, setter: noSetter, getter: dateStringGetter }, - 'last_sender_key': { - entity_field: 'lastSenderKey', - request_field_name: 'last_sender_key', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "last_sender_key": { + entity_field: "lastSenderKey", + request_field_name: "last_sender_key", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter diff --git a/src/Entities/Domains.ts b/src/Entities/Domains.ts index bb896081..75b37ea2 100755 --- a/src/Entities/Domains.ts +++ b/src/Entities/Domains.ts @@ -11,30 +11,30 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; -import { AuthToken } from '@Entities/AuthToken'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { DomainEntity } from '@Entities/DomainEntity'; -import { DomainFields, CheckDomainFields } from '@Entities/DomainFields'; +import Config from "@Base/config"; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { AuthToken } from "@Entities/AuthToken"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { DomainEntity } from "@Entities/DomainEntity"; +import { DomainFields, CheckDomainFields } from "@Entities/DomainFields"; -import { ValidateResponse } from '@Route-Tools/EntityFieldDefn'; -import { getEntityField, setEntityField, getEntityUpdateForField } from '@Route-Tools/GetterSetter'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; -import { Maturity } from '@Entities/Sets/Maturity'; +import { ValidateResponse } from "@Route-Tools/EntityFieldDefn"; +import { getEntityField, setEntityField, getEntityUpdateForField } from "@Route-Tools/GetterSetter"; -import { createObject, getObject, getObjects, countObjects, updateObjectFields, deleteOne } from '@Tools/Db'; +import { Maturity } from "@Entities/Sets/Maturity"; -import { GenUUID, IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { createObject, getObject, getObjects, countObjects, updateObjectFields, deleteOne } from "@Tools/Db"; -export let domainCollection = 'domains'; +import { GenUUID, IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; + +export const domainCollection = "domains"; // Initialize domain management. // Periodic checks on liveness of domains and resetting of values if not talking @@ -43,45 +43,51 @@ export function initDomains(): void { // Validate the fields are all set (DEBUG DEGUG for problems with circular references) CheckDomainFields(); - setInterval( async () => { + setInterval(async () => { // Find domains that are not heartbeating and reset activity if not talking for await (const aDomain of Domains.enumerateAsync(new GenericFilter( - { - 'timeOfLastHeartbeat': { '$lt': Domains.dateWhenNotActive() }, - '$or': [ - { 'numUsers': { '$gt': 0 } }, - { 'anonUsers': { '$gt': 0 } }, - { 'active': true } - ] - }) ) ) { + { + "timeOfLastHeartbeat": { "$lt": Domains.dateWhenNotActive() }, + "$or": [ + { "numUsers": { "$gt": 0 } }, + { "anonUsers": { "$gt": 0 } }, + { "active": true } + ] + }))) { Logger.info(`Domains: domain ${aDomain.name} not heartbeating. Zeroing users.`); aDomain.numUsers = 0; aDomain.anonUsers = 0; aDomain.active = false; const updates: VKeyedCollection = { - 'numUsers': 0, - 'anonUsers': 0, - 'active': false + "numUsers": 0, + "anonUsers": 0, + "active": false }; void Domains.updateEntityFields(aDomain, updates); - }; - }, 1000 * Config['metaverse-server']['domain-seconds-check-if-online']); -}; + } + }, 1000 * Config["metaverse-server"]["domain-seconds-check-if-online"]); +} export const Domains = { async getDomainWithId(pDomainId: string): Promise { - return IsNullOrEmpty(pDomainId) ? null : getObject(domainCollection, - new GenericFilter({ 'id': pDomainId })); + return IsNullOrEmpty(pDomainId) + ? null + : getObject(domainCollection, + new GenericFilter({ "id": pDomainId })); }, async getDomainWithAPIKey(pApiKey: string): Promise { - return IsNullOrEmpty(pApiKey) ? null : getObject(domainCollection, - new GenericFilter({ 'apiKey': pApiKey })); + return IsNullOrEmpty(pApiKey) + ? null + : getObject(domainCollection, + new GenericFilter({ "apiKey": pApiKey })); }, async getDomainWithSenderKey(pSenderKey: string): Promise { - return IsNullOrEmpty(pSenderKey) ? null : getObject(domainCollection, - new GenericFilter({ 'lastSenderKey': pSenderKey })); + return IsNullOrEmpty(pSenderKey) + ? null + : getObject(domainCollection, + new GenericFilter({ "lastSenderKey": pSenderKey })); }, - async addDomain(pDomainEntity: DomainEntity) : Promise { + async addDomain(pDomainEntity: DomainEntity): Promise { Logger.info(`Domains: creating domain ${pDomainEntity.name}, id=${pDomainEntity.id}`); return IsNullOrEmpty(pDomainEntity) ? null : createObject(domainCollection, pDomainEntity); }, @@ -95,9 +101,9 @@ export const Domains = { newDomain.maturity = Maturity.UNRATED; return newDomain; }, - async removeDomain(pDomainEntity: DomainEntity) : Promise { + async removeDomain(pDomainEntity: DomainEntity): Promise { Logger.info(`Domains: removing domain ${pDomainEntity.name}, id=${pDomainEntity.id}`); - return deleteOne(domainCollection, new GenericFilter({ 'id': pDomainEntity.id }) ); + return deleteOne(domainCollection, new GenericFilter({ "id": pDomainEntity.id })); }, // When removing a domain, other tables need cleaning up async removeDomainContext(pDomainEntity: DomainEntity): Promise { @@ -105,25 +111,25 @@ export const Domains = { // Don't delete the associated Places. Issue #27 was a requests to keep Places. // This creates places that point at domains that don't exist // await Places.removeMany(new GenericFilter({ 'domainId': pDomainEntity.id })); - return; + }, // Get the value of a domain field with the fieldname. // Checks to make sure the getter has permission to get the values. // Returns the value. Could be 'undefined' whether the requestor doesn't have permissions or that's // the actual field value. async getField(pAuthToken: AuthToken, pDomain: DomainEntity, - pField: string, pRequestingAccount?: AccountEntity): Promise { + pField: string, pRequestingAccount?: AccountEntity): Promise { return getEntityField(DomainFields, pAuthToken, pDomain, pField, pRequestingAccount); }, // Set a domain field with the fieldname and a value. // Checks to make sure the setter has permission to set. // Returns 'true' if the value was set and 'false' if the value could not be set. async setField(pAuthToken: AuthToken, // authorization for making this change - pDomain: DomainEntity, // the domain being changed - pField: string, pVal: any, // field being changed and the new value - pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known - pUpdates?: VKeyedCollection // where to record updates made (optional) - ): Promise { + pDomain: DomainEntity, // the domain being changed + pField: string, pVal: any, // field being changed and the new value + pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known + pUpdates?: VKeyedCollection // where to record updates made (optional) + ): Promise { return setEntityField(DomainFields, pAuthToken, pDomain, pField, pVal, pRequestingAccount, pUpdates); }, // Generate an 'update' block for the specified field or fields. @@ -132,7 +138,7 @@ export const Domains = { // we want the actual value (whatever it is) to go into the database. // If an existing VKeyedCollection is passed, it is added to an returned. getUpdateForField(pDomain: DomainEntity, - pField: string | string[], pExisting?: VKeyedCollection): VKeyedCollection { + pField: string | string[], pExisting?: VKeyedCollection): VKeyedCollection { return getEntityUpdateForField(DomainFields, pDomain, pField, pExisting); }, // Return the number of domains that match the criteria @@ -140,20 +146,20 @@ export const Domains = { return countObjects(domainCollection, pCriteria); }, async *enumerateAsync(pPager: CriteriaFilter, - pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { + pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { for await (const domain of getObjects(domainCollection, pPager, pInfoer, pScoper)) { yield domain; - }; + } // return getObjects(domainCollection, pCriteria, pPager); // not sure why this doesn't work }, // The contents of this entity have been updated async updateEntityFields(pEntity: DomainEntity, pFields: VKeyedCollection): Promise { return updateObjectFields(domainCollection, - new GenericFilter({ 'id': pEntity.id }), pFields); + new GenericFilter({ "id": pEntity.id }), pFields); }, // Return the Date when an domain is considered inactive dateWhenNotActive(): Date { - const notActiveTime = new Date(Date.now() - (1000 * Config["metaverse-server"]["domain-seconds-until-offline"] ) ); + const notActiveTime = new Date(Date.now() - 1000 * Config["metaverse-server"]["domain-seconds-until-offline"]); return notActiveTime; }, // Return 'true' if the passed string could be a domainId. Used as a precheck before querying the Db. diff --git a/src/Entities/Entity.ts b/src/Entities/Entity.ts index 898c895c..295082d9 100755 --- a/src/Entities/Entity.ts +++ b/src/Entities/Entity.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; // import { createObject, getObject, getObjects, updateObjectFields, deleteOne, noCaseCollation } from '@Tools/Db'; // import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; @@ -47,5 +46,4 @@ export abstract class Entity { }; }; */ -}; - +} diff --git a/src/Entities/EntityFilters/AccountFilterInfo.ts b/src/Entities/EntityFilters/AccountFilterInfo.ts index b7e2ff36..8b19ec0b 100755 --- a/src/Entities/EntityFilters/AccountFilterInfo.ts +++ b/src/Entities/EntityFilters/AccountFilterInfo.ts @@ -11,18 +11,18 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Request } from 'express'; -import { Accounts } from '@Entities/Accounts'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { Domains } from '@Entities/Domains'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; +import { Request } from "express"; +import { Accounts } from "@Entities/Accounts"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { Domains } from "@Entities/Domains"; -import { IsNullOrEmpty } from '@Tools/Misc'; -import { SArray } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; + +import { IsNullOrEmpty } from "@Tools/Misc"; +import { SArray } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; // Process a request that wants to filter Account collection with parameters: @@ -31,178 +31,176 @@ import { Logger } from '@Tools/Logging'; // &search=wildcardSearchString export class AccountFilterInfo extends CriteriaFilter { - private _requestingAccount: AccountEntity; - - private _filter: string; // comma list of "all", "friends", "connections" - private _findFriends: boolean = false; - private _friendsList: string[]; - private _findConnections: boolean = false; - private _connectionsList: string[]; - private _findDomain: boolean = false; - private _targetDomain: string; - - private _status: string; // comma list of "online" - private _findOnline: boolean = false; - - private _search: string; // specific name to look for? - private _findMatch: string; - - // Set to 'true' if the pagination was passed in the criteria query parameters - private _doingQuery: boolean = false; - - public constructor() { - super(); - return; - } - - // Passed the request, get the filter parameters from the query. - // Here we pre-process the parameters to make the DB query construction quicker. - // filter=connections|friends|all - // status=online|domainId - // search=wildcardSearchString - public parametersFromRequest(pRequest: Request) : void { - this._requestingAccount = pRequest.vAuthAccount; - - try { - // Comma separated list of attribute criteria. - if (typeof(pRequest.query.filter) === 'string') { - this._filter = pRequest.query.filter; - const filterPieces = this._filter.split(','); - filterPieces.forEach( filterClass => { - switch (filterClass) { - case 'all': - this._findFriends = false; - this._findConnections = false; - break; - case 'friends': - this._findFriends = true; - break; - case 'connections': - this._findConnections = true; - break; - default: - break; - }; - }); - if (this._findFriends && this._requestingAccount) { - this._friendsList = this._requestingAccount.friends; - if (IsNullOrEmpty(this._friendsList)) { - // asking to filter on friends but has no friends - this._findFriends = false; // if no friends, don't search for it - }; - }; - if (this._findConnections && this._requestingAccount) { - this._connectionsList = this._requestingAccount.connections; - if (IsNullOrEmpty(this._connectionsList)) { - // asking to filter on friends but has no friends - this._findConnections = false; // if no friends, don't search for it - }; - }; - }; - - // Commas separated list of target's status. - // Currently, the only selection is 'online'. - if (typeof(pRequest.query.status) === 'string') { - this._status = pRequest.query.status; - const statusPieces = this._status.split(','); - statusPieces.forEach( statusClass => { - switch (statusClass) { - case 'online': - this._findOnline = true; - break; - default: - // There is the version where the status is a domainId which - // limits the accounts to the specified domain. - // Also presumes 'online'. - if (Domains.couldBeDomainId(statusClass)) { - this._findDomain = true; - this._findOnline = true; - this._targetDomain = statusClass; - }; - break; - }; - }); - }; - - this._search = String(pRequest.query.search); - - Logger.cdebug('query-detail', `AccountFilterInfo.parametersFromRequest: findFriends=${this._findFriends}, findConn=${this._findConnections}, findOnline=${this._findOnline}, findDomain=${this._findDomain}`); + private _requestingAccount: AccountEntity; + + private _filter: string; // comma list of "all", "friends", "connections" + private _findFriends = false; + private _friendsList: string[]; + private _findConnections = false; + private _connectionsList: string[]; + private _findDomain = false; + private _targetDomain: string; + + private _status: string; // comma list of "online" + private _findOnline = false; + + private _search: string; // specific name to look for? + private _findMatch: string; + + // Set to 'true' if the pagination was passed in the criteria query parameters + private _doingQuery = false; + + public constructor() { + super(); + } - catch (e) { - Logger.error('AccountFilterInfo: parameters from request: exception: ' + e); + + // Passed the request, get the filter parameters from the query. + // Here we pre-process the parameters to make the DB query construction quicker. + // filter=connections|friends|all + // status=online|domainId + // search=wildcardSearchString + public parametersFromRequest(pRequest: Request): void { + this._requestingAccount = pRequest.vAuthAccount; + + try { + // Comma separated list of attribute criteria. + if (typeof pRequest.query.filter === "string") { + this._filter = pRequest.query.filter; + const filterPieces = this._filter.split(","); + filterPieces.forEach((filterClass) => { + switch (filterClass) { + case "all": + this._findFriends = false; + this._findConnections = false; + break; + case "friends": + this._findFriends = true; + break; + case "connections": + this._findConnections = true; + break; + default: + break; + } + }); + if (this._findFriends && this._requestingAccount) { + this._friendsList = this._requestingAccount.friends; + if (IsNullOrEmpty(this._friendsList)) { + // asking to filter on friends but has no friends + this._findFriends = false; // if no friends, don't search for it + } + } + if (this._findConnections && this._requestingAccount) { + this._connectionsList = this._requestingAccount.connections; + if (IsNullOrEmpty(this._connectionsList)) { + // asking to filter on friends but has no friends + this._findConnections = false; // if no friends, don't search for it + } + } + } + + // Commas separated list of target's status. + // Currently, the only selection is 'online'. + if (typeof pRequest.query.status === "string") { + this._status = pRequest.query.status; + const statusPieces = this._status.split(","); + statusPieces.forEach((statusClass) => { + switch (statusClass) { + case "online": + this._findOnline = true; + break; + default: + // There is the version where the status is a domainId which + // limits the accounts to the specified domain. + // Also presumes 'online'. + if (Domains.couldBeDomainId(statusClass)) { + this._findDomain = true; + this._findOnline = true; + this._targetDomain = statusClass; + } + break; + } + }); + } + + this._search = String(pRequest.query.search); + + Logger.cdebug("query-detail", `AccountFilterInfo.parametersFromRequest: findFriends=${this._findFriends}, findConn=${this._findConnections}, findOnline=${this._findOnline}, findDomain=${this._findDomain}`); + } catch (e) { + Logger.error("AccountFilterInfo: parameters from request: exception: " + e); + } } - }; - - // Add any parameters to the response - public addResponseFields(pRequest: Request) { - return; - }; - - // Passed (what should be) an AccountEntity, test if the filters - // think it's passable. - // Return 'true' of this account fits the search criteria. - public criteriaTest(pThingy: any): boolean { - let ret = false; - if (this._doingQuery) { - ret = true; + + // Add any parameters to the response + public addResponseFields(pRequest: Request) { + } - else { - let filterSelect = false - if (this._findFriends) { - if (pThingy.hasOwnProperty('friends')) { - for (const friend of this._friendsList) { - if (SArray.has(pThingy.friends, friend)) { - filterSelect = true; - break; - }; - }; - }; - }; - if (this._findConnections) { - if (pThingy.hasOwnProperty('connections')) { - for (const connection of this._connectionsList) { - if (SArray.has(pThingy.connections, connection)) { - filterSelect = true; - break; - }; - }; - }; - }; - const statusSelect = false; - if (this._findOnline) { - if (pThingy.hasOwnProperty('timeOfLastHeartbeat')) { - ret = Accounts.isOnline(pThingy as AccountEntity); - }; - }; - ret = filterSelect && statusSelect; + + // Passed (what should be) an AccountEntity, test if the filters + // think it's passable. + // Return 'true' of this account fits the search criteria. + public criteriaTest(pThingy: any): boolean { + let ret = false; + if (this._doingQuery) { + ret = true; + } else { + let filterSelect = false; + if (this._findFriends) { + if (pThingy.hasOwnProperty("friends")) { + for (const friend of this._friendsList) { + if (SArray.has(pThingy.friends, friend)) { + filterSelect = true; + break; + } + } + } + } + if (this._findConnections) { + if (pThingy.hasOwnProperty("connections")) { + for (const connection of this._connectionsList) { + if (SArray.has(pThingy.connections, connection)) { + filterSelect = true; + break; + } + } + } + } + const statusSelect = false; + if (this._findOnline) { + if (pThingy.hasOwnProperty("timeOfLastHeartbeat")) { + ret = Accounts.isOnline(pThingy as AccountEntity); + } + } + ret = filterSelect && statusSelect; + } + return ret; } - return ret; - }; public async criteriaTestAsync(pThingy: any): Promise { return this.criteriaTest(pThingy); - }; - - // Return the MongoDB query parameters for the search criteria - public criteriaParameters(): any { - this._doingQuery = true; - const criteria:any = {}; - if (this._findFriends) { - criteria.friends = { '$in': this._friendsList } - }; - if (this._findConnections) { - criteria.connections = { '$in': this._connectionsList } - }; - if (this._findOnline) { - criteria.timeOfLastHeartbeat = { '$gte': Accounts.dateWhenNotOnline() } - }; - if (this._findDomain) { - criteria.locationDomainId = this._targetDomain - }; - return criteria; - }; - - public sortCriteriaParameters(): any { - return null; - }; -}; \ No newline at end of file + } + + // Return the MongoDB query parameters for the search criteria + public criteriaParameters(): any { + this._doingQuery = true; + const criteria: any = {}; + if (this._findFriends) { + criteria.friends = { "$in": this._friendsList }; + } + if (this._findConnections) { + criteria.connections = { "$in": this._connectionsList }; + } + if (this._findOnline) { + criteria.timeOfLastHeartbeat = { "$gte": Accounts.dateWhenNotOnline() }; + } + if (this._findDomain) { + criteria.locationDomainId = this._targetDomain; + } + return criteria; + } + + public sortCriteriaParameters(): any { + return null; + } +} diff --git a/src/Entities/EntityFilters/AccountScopeFilter.ts b/src/Entities/EntityFilters/AccountScopeFilter.ts index 5ff27d14..ddb5a468 100755 --- a/src/Entities/EntityFilters/AccountScopeFilter.ts +++ b/src/Entities/EntityFilters/AccountScopeFilter.ts @@ -11,14 +11,14 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Request } from 'express'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { Accounts } from '@Entities/Accounts'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { Logger } from '@Tools/Logging'; -import { prototype } from 'events'; + +import { Request } from "express"; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { Accounts } from "@Entities/Accounts"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { Logger } from "@Tools/Logging"; +import { prototype } from "events"; // AccountScopeFilter filters a query stream to the accounts the requestor // can look at. That is, a person can normally see only the domains @@ -26,90 +26,90 @@ import { prototype } from 'events'; // &asAdmin=true // &acct=targetAccountId export class AccountScopeFilter extends CriteriaFilter { - private _asAdmin = false; - private _accessingAcct: AccountEntity; - private _field: string; - private _targetAcct: string = undefined; + private _asAdmin = false; + private _accessingAcct: AccountEntity; + private _field: string; + private _targetAcct: string = undefined; + + // Set to 'true' if the pagination was passed in the criteria query parameters + private _doingQuery = false; - // Set to 'true' if the pagination was passed in the criteria query parameters - private _doingQuery: boolean = false; + // Create a new filter and specify the requesting account and + // field in target entity to check for match. + // "accountId" works for most things but scoping domains by their owners + // requires checking "sponsorAccountId"> + public constructor(pRequestorAccount: AccountEntity, pField = "id") { + super(); + this._accessingAcct = pRequestorAccount; + this._field = pField; - // Create a new filter and specify the requesting account and - // field in target entity to check for match. - // "accountId" works for most things but scoping domains by their owners - // requires checking "sponsorAccountId"> - public constructor(pRequestorAccount: AccountEntity, pField: string = 'id') { - super(); - this._accessingAcct = pRequestorAccount; - this._field = pField; - return; - } - public parametersFromRequest(pRequest: Request) : void { - try { - if (pRequest.query.hasOwnProperty('asAdmin')) { - const askingForAdmin = pRequest.query.asAdmin === 'true'; - if (askingForAdmin) { - if (Accounts.isAdmin(this._accessingAcct)) { - this._asAdmin = true; - }; - }; - }; - // The administrator can specify an account to limit requests to - if (pRequest.query.acct) { - this._targetAcct = pRequest.query.account as string; - }; - Logger.cdebug('query-detail', `AccountScopeFilter.parametersFromRequest: asAdmin=${this._asAdmin}, target=${this._targetAcct}`); } - catch (e) { - Logger.error('AccountScopeFilter: parameters from request: exception: ' + e); - }; - }; - // Add any parameters to the response - public addResponseFields(pRequest: Request) { - return; - }; + public parametersFromRequest(pRequest: Request): void { + try { + if (pRequest.query.hasOwnProperty("asAdmin")) { + const askingForAdmin = pRequest.query.asAdmin === "true"; + if (askingForAdmin) { + if (Accounts.isAdmin(this._accessingAcct)) { + this._asAdmin = true; + } + } + } + // The administrator can specify an account to limit requests to + if (pRequest.query.acct) { + this._targetAcct = pRequest.query.account as string; + } + Logger.cdebug("query-detail", `AccountScopeFilter.parametersFromRequest: asAdmin=${this._asAdmin}, target=${this._targetAcct}`); + } catch (e) { + Logger.error("AccountScopeFilter: parameters from request: exception: " + e); + } + } + + // Add any parameters to the response + public addResponseFields(pRequest: Request) { + + } - // Return if we've found admin enabling parameters - public AsAdmin(): boolean { - return this._asAdmin; - }; + // Return if we've found admin enabling parameters + public AsAdmin(): boolean { + return this._asAdmin; + } - public criteriaTest(pToTest: any): boolean { - if (! this._doingQuery) { - if (this._asAdmin) { - if (this._targetAcct) { - return typeof(pToTest[this._field]) !== 'undefined' + public criteriaTest(pToTest: any): boolean { + if (!this._doingQuery) { + if (this._asAdmin) { + if (this._targetAcct) { + return typeof pToTest[this._field] !== "undefined" && pToTest[this._field] === this._targetAcct; + } + return true; + } + return typeof pToTest[this._field] !== "undefined" + && pToTest[this._field] === this._accessingAcct.id; } return true; - }; - return typeof(pToTest[this._field]) !== 'undefined' - && pToTest[this._field] === this._accessingAcct.id; } - return true; - }; - public async criteriaTestAsync(pThingy: any): Promise { - return this.criteriaTest(pThingy); - }; + public async criteriaTestAsync(pThingy: any): Promise { + return this.criteriaTest(pThingy); + } - public criteriaParameters(): any { - this._doingQuery = true; - const criteria: any = {}; - // If not an admin, the found items must match the id of the requestor - if (! this._asAdmin) { - criteria[this._field] = this._accessingAcct.id; - // Logger.debug(`AccountScopeFilter.criteriaParameters: not admin so limiting to account ${this._accessingAcct.username}`); - }; - // If an admin and requested scope of target account, do that check - if (this._asAdmin && typeof(this._targetAcct) !== 'undefined') { - criteria[this._field] = this._targetAcct + public criteriaParameters(): any { + this._doingQuery = true; + const criteria: any = {}; + // If not an admin, the found items must match the id of the requestor + if (!this._asAdmin) { + criteria[this._field] = this._accessingAcct.id; + // Logger.debug(`AccountScopeFilter.criteriaParameters: not admin so limiting to account ${this._accessingAcct.username}`); + } + // If an admin and requested scope of target account, do that check + if (this._asAdmin && typeof this._targetAcct !== "undefined") { + criteria[this._field] = this._targetAcct; + } + return criteria; } - return criteria; - }; - public sortCriteriaParameters(): any { - return null; - }; -}; \ No newline at end of file + public sortCriteriaParameters(): any { + return null; + } +} diff --git a/src/Entities/EntityFilters/CriteriaFilter.ts b/src/Entities/EntityFilters/CriteriaFilter.ts index 80d19ae8..b9bf8bff 100755 --- a/src/Entities/EntityFilters/CriteriaFilter.ts +++ b/src/Entities/EntityFilters/CriteriaFilter.ts @@ -11,9 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Request } from 'express'; + +import { Request } from "express"; // Criteria filters are used to filter collections of entities. // They work by taking an enumeration stream and returning a stream @@ -34,25 +34,25 @@ import { Request } from 'express'; // response. export abstract class CriteriaFilter { - // Take a request and extract filter parameters - abstract parametersFromRequest(pRequest: Request): void; + // Take a request and extract filter parameters + abstract parametersFromRequest(pRequest: Request): void; - // Adds additional fields to the response - abstract addResponseFields(pRequest: Request): void; + // Adds additional fields to the response + abstract addResponseFields(pRequest: Request): void; - // Test a thing and return 'true' if it should be included in the set - abstract criteriaTest(pThingy: any): boolean; + // Test a thing and return 'true' if it should be included in the set + abstract criteriaTest(pThingy: any): boolean; - // A async version of criteriaTest. This is used externally to the - // database filtering and should go away when the aggregate pipeline - // is implemented. - abstract criteriaTestAsync(pThingy: any): Promise; + // A async version of criteriaTest. This is used externally to the + // database filtering and should go away when the aggregate pipeline + // is implemented. + abstract criteriaTestAsync(pThingy: any): Promise; - // Return Mongodb criteria for the search query - // This changes what 'criteriaTest' returns since the testing is now - // expected to be in the query. - abstract criteriaParameters(): any; + // Return Mongodb criteria for the search query + // This changes what 'criteriaTest' returns since the testing is now + // expected to be in the query. + abstract criteriaParameters(): any; - // return Mongodb critera for sort operations - abstract sortCriteriaParameters(): any; -}; + // return Mongodb critera for sort operations + abstract sortCriteriaParameters(): any; +} diff --git a/src/Entities/EntityFilters/GenericFilter.ts b/src/Entities/EntityFilters/GenericFilter.ts index 84a9cb03..60627819 100755 --- a/src/Entities/EntityFilters/GenericFilter.ts +++ b/src/Entities/EntityFilters/GenericFilter.ts @@ -11,11 +11,11 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Request } from 'express'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; +import { Request } from "express"; + +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; // A generic filter wrapper where the caller can pass any criteria. // This is used to pass a raw MongoDB query parameter and fulfills @@ -24,41 +24,42 @@ import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; // to change. export class GenericFilter extends CriteriaFilter { - private _criteria: any; - private _sortCriteria: any; + private _criteria: any; + private _sortCriteria: any; + + public constructor(pFilterCriteria: any, pSortCriteria?: any) { + super(); + this._criteria = pFilterCriteria; + this._sortCriteria = pSortCriteria; + } - public constructor(pFilterCriteria: any, pSortCriteria?: any) { - super(); - this._criteria = pFilterCriteria; - this._sortCriteria = pSortCriteria; - } + // Take a request and extract filter parameters + public parametersFromRequest(pRequest: Request): void { - // Take a request and extract filter parameters - public parametersFromRequest(pRequest: Request): void { - return; - }; + } - // Add any parameters to the response - public addResponseFields(pRequest: Request) { - return; - }; + // Add any parameters to the response + public addResponseFields(pRequest: Request) { - // Test a thing and return 'true' if it should be included in the set - public criteriaTest(pThingy: any): boolean { - return true; - }; + } + + // Test a thing and return 'true' if it should be included in the set + public criteriaTest(pThingy: any): boolean { + return true; + } public async criteriaTestAsync(pThingy: any): Promise { return this.criteriaTest(pThingy); - }; + } + + // Return Mongodb criteria for the search query + // This changes what 'criteriaTest' returns since the testing is now + // expected to be in the query. + public criteriaParameters(): any { + return this._criteria; + } - // Return Mongodb criteria for the search query - // This changes what 'criteriaTest' returns since the testing is now - // expected to be in the query. - public criteriaParameters(): any { - return this._criteria; - }; - public sortCriteriaParameters(): any { - return this._sortCriteria; - }; -}; + public sortCriteriaParameters(): any { + return this._sortCriteria; + } +} diff --git a/src/Entities/EntityFilters/PaginationInfo.ts b/src/Entities/EntityFilters/PaginationInfo.ts index 9ba2ab9b..a19658cc 100755 --- a/src/Entities/EntityFilters/PaginationInfo.ts +++ b/src/Entities/EntityFilters/PaginationInfo.ts @@ -11,95 +11,96 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Request } from 'express'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; +import { Request } from "express"; -import { Clamp } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; + +import { Clamp } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // Limits the number of entries returned // &page=N // &per_page=N export class PaginationInfo extends CriteriaFilter { - public PageNum: number = 1; - public PerPage: number = 20; - public TotalPages: number = 1; - public TotalEntries: number = 20; - - // results from as filter operation - private _currentPage: number = 1; - private _currentItem: number = 1; - - // Set to 'true' if the pagination was passed in the criteria query parameters - private _doingQuery: boolean = false; - - public constructor(pPageNum: number = 1, pPerPage: number = 1000) { - super(); - this.PageNum = Clamp(pPageNum, 1, 1000); - this.PerPage = Clamp(pPerPage, 1, 1000); - this.TotalPages = this.PageNum; - this.TotalEntries = 0; - } - - public parametersFromRequest(pRequest: Request) : void { - if (pRequest.query.page) { - this.PageNum = Clamp(Number(pRequest.query.page), 1, 1000); - }; - if (pRequest.query.per_page) { - this.PerPage = Clamp(Number(pRequest.query.per_page), 1, 1000); - }; - this.TotalPages = this.PageNum; - this.TotalEntries = 0; + public PageNum = 1; + public PerPage = 20; + public TotalPages = 1; + public TotalEntries = 20; + + // results from as filter operation + private _currentPage = 1; + private _currentItem = 1; + + // Set to 'true' if the pagination was passed in the criteria query parameters + private _doingQuery = false; + + public constructor(pPageNum = 1, pPerPage = 1000) { + super(); + this.PageNum = Clamp(pPageNum, 1, 1000); + this.PerPage = Clamp(pPerPage, 1, 1000); + this.TotalPages = this.PageNum; + this.TotalEntries = 0; + } + + public parametersFromRequest(pRequest: Request): void { + if (pRequest.query.page) { + this.PageNum = Clamp(Number(pRequest.query.page), 1, 1000); + } + if (pRequest.query.per_page) { + this.PerPage = Clamp(Number(pRequest.query.per_page), 1, 1000); + } + this.TotalPages = this.PageNum; + this.TotalEntries = 0; // Logger.debug(`PaginstationInfo: pageNum=${this.PageNum}, perPage=${this.PerPage}`); - } - - // At end of request, pagination adds to the JSON response: - // "current_page": num, - // "total_pages": num, - // "per_page": num, - // "total_entries": num - public addResponseFields(pRequest: Request): void { - if (pRequest.vRestResp) { - pRequest.vRestResp.addAdditionalField('current_page', this.PageNum); - pRequest.vRestResp.addAdditionalField('per_page', this.PerPage); - pRequest.vRestResp.addAdditionalField('total_pages', this.TotalPages); - pRequest.vRestResp.addAdditionalField('total_entries', this.TotalEntries); - }; - }; + } - public criteriaTest(pThingy: any): boolean { - if (! this._doingQuery) { - this.TotalEntries++; - if (this._currentItem > this.PerPage) { - this._currentItem = 1; - ++this._currentPage; - }; - // Logger.debug(`Pagination: curItem=${this._currentItem}, curP=${this._currentPage}, perPg=${this.PerPage}, total=${this.TotalEntries}`); - this._currentItem++; - return this.PageNum === this._currentPage; - }; - return true; - }; + // At end of request, pagination adds to the JSON response: + // "current_page": num, + // "total_pages": num, + // "per_page": num, + // "total_entries": num + public addResponseFields(pRequest: Request): void { + if (pRequest.vRestResp) { + pRequest.vRestResp.addAdditionalField("current_page", this.PageNum); + pRequest.vRestResp.addAdditionalField("per_page", this.PerPage); + pRequest.vRestResp.addAdditionalField("total_pages", this.TotalPages); + pRequest.vRestResp.addAdditionalField("total_entries", this.TotalEntries); + } + } - public async criteriaTestAsync(pThingy: any): Promise { - return this.criteriaTest(pThingy); - }; + public criteriaTest(pThingy: any): boolean { + if (!this._doingQuery) { + this.TotalEntries++; + if (this._currentItem > this.PerPage) { + this._currentItem = 1; + ++this._currentPage; + } + // Logger.debug(`Pagination: curItem=${this._currentItem}, curP=${this._currentPage}, perPg=${this.PerPage}, total=${this.TotalEntries}`); + this._currentItem++; + return this.PageNum === this._currentPage; + } + return true; + } + + public async criteriaTestAsync(pThingy: any): Promise { + return this.criteriaTest(pThingy); + } - public criteriaParameters(): any { + public criteriaParameters(): any { // this._doingQuery = true; - return { - // '$skip': (this.PageNum - 1) * this.PerPage, - // '$limit': this.PerPage - }; - }; - public sortCriteriaParameters(): any { - return null; - }; + return { + // '$skip': (this.PageNum - 1) * this.PerPage, + // '$limit': this.PerPage + }; + } + + public sortCriteriaParameters(): any { + return null; + } - /* + /* public *filter(pToFilter: Generator) : Generator { this._currentPage = 1; this._currentItem = 1; @@ -134,4 +135,4 @@ export class PaginationInfo extends CriteriaFilter { }; }; */ -}; \ No newline at end of file +} diff --git a/src/Entities/EntityFilters/PlaceFilterInfo.ts b/src/Entities/EntityFilters/PlaceFilterInfo.ts index a219c98b..cbc52305 100644 --- a/src/Entities/EntityFilters/PlaceFilterInfo.ts +++ b/src/Entities/EntityFilters/PlaceFilterInfo.ts @@ -1,224 +1,218 @@ -// Copyright 2020 Vircadia Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -'use strict' - -import { Request } from 'express'; -import { Places } from '@Entities/Places'; -import { PlaceEntity } from '@Entities/PlaceEntity'; - -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { Maturity } from '@Entities/Sets/Maturity'; - -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; - - -// Process a request that wants to filter Account collection with parameters: -// &maturity=unrated,mature -// &tag=tag1,tag2,tag3 -// &status=online,active -// online = Place/Domain are heartbeating -// active = one or more avatars are present -// &order=ascending,decending,num_users,name -// &search=regex -export class PlaceFilterInfo extends CriteriaFilter { - - private _maturity: string[]; - private _checkIfOnline: boolean = false; - private _checkIfActive: boolean = false; - private _tags: string[]; - private _ascending: number = 1; - private _orderByName: boolean = false; - private _orderByUsers: boolean = false; - private _search: string; - - // Set to 'true' if the pagination was passed in the criteria query parameters - private _doingQuery: boolean = false; - - public constructor() { - super(); - return; - } - - // Passed the request, get the filter parameters from the query. - // Here we pre-process the parameters to make the DB query construction quicker. - public parametersFromRequest(pRequest: Request) : void { - try { - // Comma separated list of attribute criteria. - if (typeof(pRequest.query.maturity) === 'string') { - this._maturity = pRequest.query.maturity.split(','); - // Check to make sure all the pieces are legal maturity names - let allLegal = true; - for (const mat of this._maturity) { - if ( ! Maturity.KnownMaturity(mat)) { - allLegal = false; - break; - }; - }; - if (allLegal) { - if (this._maturity.includes(Maturity.UNRATED)) { - // Adding a 'null' to the set causes places with no rating to be included - this._maturity.push(null); - }; - } - else { - Logger.cdebug('query-detail', `PlaceFilterInfo.parametersFromRequest: passed undefined maturity level ${JSON.stringify(this._maturity)}`); - this._maturity = undefined; - }; - Logger.cdebug('query-detail', `PlaceFilterInfo.parametersFromRequest: maturity=${JSON.stringify(this._maturity)}`); - }; - - // Commas separated list of place tags - if (typeof(pRequest.query.tag) === 'string') { - this._tags = pRequest.query.tag.split(','); - Logger.cdebug('query-detail', `PlaceFilterInfo.parametersFromRequest: tags=${JSON.stringify(this._tags)}`); - }; - - if (typeof(pRequest.query.status) === 'string') { - const statuses = pRequest.query.status.split(','); - for (const stat of statuses) { - switch (stat) { - case 'online': - this._checkIfOnline = true; - break; - case 'active': - this._checkIfActive = true; - break; - default: - break; - }; - }; - // Logger.cdebug('query-detail', `PlaceFilterInfo.parametersFromRequest: status=${JSON.stringify(this._active)}`); - }; - - if (typeof(pRequest.query.order) === 'string') { - const orderings = pRequest.query.order.split(','); - for (const order of orderings) { - switch (order) { - case 'ascending': - this._ascending = 1; - break; - case 'decending': - this._ascending = -1; - break; - case 'num_users': - this._orderByUsers = true; - break; - case 'name': - this._orderByName = true; - break; - default: - break; - }; - }; - Logger.cdebug('query-detail', `PlaceFilterInfo.parametersFromRequest: order: ${this._ascending}, users: ${this._orderByUsers}, name: ${this._orderByName}`); - }; - - if (typeof(pRequest.query.search) === 'string') { - const searcher = pRequest.query.search; - this._search = searcher; - Logger.cdebug('query-detail', `PlaceFilterInfo.parametersFromRequest: search: ${this._search}`); - }; - } - catch (e) { - Logger.error('PlaceFilterInfo: parameters from request: exception: ' + e); - }; - }; - - // Add any parameters to the response - public addResponseFields(pRequest: Request) { - return; - }; - - // Passed (what should be) an AccountEntity, test if the filters - // think it's passable. - // Return 'true' of this account fits the search criteria. - public criteriaTest(pThingy: any): boolean { - let ret = false; - if (this._doingQuery) { - ret = true; - } - else { - if (this._maturity) { - if (pThingy.hasOwnProperty('maturity')) { - if (this._maturity.includes((pThingy as PlaceEntity).maturity)) { - ret = true; - }; - } - else { - if (this._maturity.includes(Maturity.UNRATED)) { - ret = true; - }; - }; - }; - if (!ret && this._tags && pThingy.hasOwnProperty('tags')) { - for (const aTag of this._tags) { - if ((pThingy as PlaceEntity).tags.includes(aTag)) { - ret = true; - break; - }; - }; - }; - if (!ret && this._search && pThingy.hasOwnProperty('name')) { - const matches = (pThingy as PlaceEntity).name.match(this._search); - ret = matches && (matches.length === 1); - }; - }; - return ret; - }; - - public async criteriaTestAsync(pThingy: any): Promise { - return this.criteriaTest(pThingy); - }; - - // Return the MongoDB query parameters for the search criteria - public criteriaParameters(): any { - this._doingQuery = true; - const criteria:VKeyedCollection = {}; - if (this._maturity) { - /* tslint:disable-next-line */ - criteria['maturity'] = { '$in': this._maturity }; - }; - if (this._checkIfActive) { - criteria.currentAttendance = { '$gt': 0 }; - }; - if (this._checkIfOnline) { - criteria.lastActivity = { '$gte': Places.dateWhenNotActive() }; - }; - if (this._tags) { - /* tslint:disable-next-line */ - criteria['tags'] = { '$in': this._tags }; - }; - if (this._search) { - /* tslint:disable-next-line */ - criteria['name'] = { '$regex': this._search, '$options': 'i' }; - }; - return criteria; - }; - - public sortCriteriaParameters(): any { - if (this._orderByUsers || this._orderByName) { - const sorting: any = {}; - if (this._orderByUsers) { - sorting.attendance = this._ascending; - } - if (this._orderByName) { - sorting.name = this._ascending; - } - Logger.cdebug('query-detail', `PlaceFilterInfo.sortCriteriaParameters: ${JSON.stringify(sorting)}`); - return sorting; - } - return null; - }; -}; +// Copyright 2020 Vircadia Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import { Request } from "express"; +import { Places } from "@Entities/Places"; +import { PlaceEntity } from "@Entities/PlaceEntity"; + +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { Maturity } from "@Entities/Sets/Maturity"; + +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; + + +// Process a request that wants to filter Account collection with parameters: +// &maturity=unrated,mature +// &tag=tag1,tag2,tag3 +// &status=online,active +// online = Place/Domain are heartbeating +// active = one or more avatars are present +// &order=ascending,decending,num_users,name +// &search=regex +export class PlaceFilterInfo extends CriteriaFilter { + + private _maturity: string[]; + private _checkIfOnline = false; + private _checkIfActive = false; + private _tags: string[]; + private _ascending = 1; + private _orderByName = false; + private _orderByUsers = false; + private _search: string; + + // Set to 'true' if the pagination was passed in the criteria query parameters + private _doingQuery = false; + + public constructor() { + super(); + + } + + // Passed the request, get the filter parameters from the query. + // Here we pre-process the parameters to make the DB query construction quicker. + public parametersFromRequest(pRequest: Request): void { + try { + // Comma separated list of attribute criteria. + if (typeof pRequest.query.maturity === "string") { + this._maturity = pRequest.query.maturity.split(","); + // Check to make sure all the pieces are legal maturity names + let allLegal = true; + for (const mat of this._maturity) { + if (!Maturity.KnownMaturity(mat)) { + allLegal = false; + break; + } + } + if (allLegal) { + if (this._maturity.includes(Maturity.UNRATED)) { + // Adding a 'null' to the set causes places with no rating to be included + this._maturity.push(null); + } + } else { + Logger.cdebug("query-detail", `PlaceFilterInfo.parametersFromRequest: passed undefined maturity level ${JSON.stringify(this._maturity)}`); + this._maturity = undefined; + } + Logger.cdebug("query-detail", `PlaceFilterInfo.parametersFromRequest: maturity=${JSON.stringify(this._maturity)}`); + } + + // Commas separated list of place tags + if (typeof pRequest.query.tag === "string") { + this._tags = pRequest.query.tag.split(","); + Logger.cdebug("query-detail", `PlaceFilterInfo.parametersFromRequest: tags=${JSON.stringify(this._tags)}`); + } + + if (typeof pRequest.query.status === "string") { + const statuses = pRequest.query.status.split(","); + for (const stat of statuses) { + switch (stat) { + case "online": + this._checkIfOnline = true; + break; + case "active": + this._checkIfActive = true; + break; + default: + break; + } + } + // Logger.cdebug('query-detail', `PlaceFilterInfo.parametersFromRequest: status=${JSON.stringify(this._active)}`); + } + + if (typeof pRequest.query.order === "string") { + const orderings = pRequest.query.order.split(","); + for (const order of orderings) { + switch (order) { + case "ascending": + this._ascending = 1; + break; + case "decending": + this._ascending = -1; + break; + case "num_users": + this._orderByUsers = true; + break; + case "name": + this._orderByName = true; + break; + default: + break; + } + } + Logger.cdebug("query-detail", `PlaceFilterInfo.parametersFromRequest: order: ${this._ascending}, users: ${this._orderByUsers}, name: ${this._orderByName}`); + } + + if (typeof pRequest.query.search === "string") { + const searcher = pRequest.query.search; + this._search = searcher; + Logger.cdebug("query-detail", `PlaceFilterInfo.parametersFromRequest: search: ${this._search}`); + } + } catch (e) { + Logger.error("PlaceFilterInfo: parameters from request: exception: " + e); + } + } + + // Add any parameters to the response + public addResponseFields(pRequest: Request) { + + } + + // Passed (what should be) an AccountEntity, test if the filters + // think it's passable. + // Return 'true' of this account fits the search criteria. + public criteriaTest(pThingy: any): boolean { + let ret = false; + if (this._doingQuery) { + ret = true; + } else { + if (this._maturity) { + if (pThingy.hasOwnProperty("maturity")) { + if (this._maturity.includes((pThingy as PlaceEntity).maturity)) { + ret = true; + } + } else if (this._maturity.includes(Maturity.UNRATED)) { + ret = true; + } + } + if (!ret && this._tags && pThingy.hasOwnProperty("tags")) { + for (const aTag of this._tags) { + if ((pThingy as PlaceEntity).tags.includes(aTag)) { + ret = true; + break; + } + } + } + if (!ret && this._search && pThingy.hasOwnProperty("name")) { + const matches = (pThingy as PlaceEntity).name.match(this._search); + ret = matches && matches.length === 1; + } + } + return ret; + } + + public async criteriaTestAsync(pThingy: any): Promise { + return this.criteriaTest(pThingy); + } + + // Return the MongoDB query parameters for the search criteria + public criteriaParameters(): any { + this._doingQuery = true; + const criteria: VKeyedCollection = {}; + if (this._maturity) { + // eslint-disable-next-line + criteria['maturity'] = { '$in': this._maturity }; + } + if (this._checkIfActive) { + criteria.currentAttendance = { "$gt": 0 }; + } + if (this._checkIfOnline) { + criteria.lastActivity = { "$gte": Places.dateWhenNotActive() }; + } + if (this._tags) { + // eslint-disable-next-line + criteria['tags'] = { '$in': this._tags }; + } + if (this._search) { + // eslint-disable-next-line + criteria['name'] = { '$regex': this._search, '$options': 'i' }; + } + return criteria; + } + + public sortCriteriaParameters(): any { + if (this._orderByUsers || this._orderByName) { + const sorting: any = {}; + if (this._orderByUsers) { + sorting.attendance = this._ascending; + } + if (this._orderByName) { + sorting.name = this._ascending; + } + Logger.cdebug("query-detail", `PlaceFilterInfo.sortCriteriaParameters: ${JSON.stringify(sorting)}`); + return sorting; + } + return null; + } +} diff --git a/src/Entities/EntityFilters/RequestScopeFilter.ts b/src/Entities/EntityFilters/RequestScopeFilter.ts index 165c76e2..9aadb6b8 100755 --- a/src/Entities/EntityFilters/RequestScopeFilter.ts +++ b/src/Entities/EntityFilters/RequestScopeFilter.ts @@ -11,118 +11,119 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Request } from 'express'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; +import { Request } from "express"; -import { Accounts } from '@Entities/Accounts'; -import { AccountEntity } from '@Entities/AccountEntity'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; -import { SimpleObject } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { Accounts } from "@Entities/Accounts"; +import { AccountEntity } from "@Entities/AccountEntity"; + +import { SimpleObject } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // Controls access to Requests // &asAdmin=true // &acct=targetAccountId export class RequestScopeFilter extends CriteriaFilter { - // If an admin and the optional target account - private _asAdmin = false; - private _targetAcct: string = undefined; - - // Who is making the request - private _accessingAcct: AccountEntity; - - // The two fields checked can be passed. These are the defaults - private _fromId = 'requestingAccountId'; - private _toId = 'targetAccountId'; - - // Set to 'true' if the filtering was passed in the criteria query parameters - private _doingQuery: boolean = false; - - // Create a new filter and specify the requesting account and - // field in target entity to check for match. - // "accountId" works for most things but scoping domains by their owners - // requires checking "sponsorAccountId"> - public constructor(pRequestorAccount: AccountEntity, - pFrom: string = 'requestingAccountId', pTo: string = 'targetAccountId') { - super(); - this._accessingAcct = pRequestorAccount; - this._fromId = pFrom; - this._toId = pTo; - return; - } - // Process query parameters - public parametersFromRequest(pRequest: Request) : void { - try { - if (pRequest.query.hasOwnProperty('asAdmin')) { - const askingForAdmin = pRequest.query.asAdmin === 'true'; - if (askingForAdmin) { - if (Accounts.isAdmin(this._accessingAcct)) { - this._asAdmin = true; - }; - }; - }; - // The administrator can specify an account to limit requests to - if (pRequest.query.acct && typeof(pRequest.query.acct) === 'string') { - this._targetAcct = pRequest.query.account as string; - }; - Logger.cdebug('query-detail', `RequestScopeFilter.parametersFromRequest: asAdmin=${this._asAdmin}, target=${this._targetAcct}`); + // If an admin and the optional target account + private _asAdmin = false; + private _targetAcct: string = undefined; + + // Who is making the request + private _accessingAcct: AccountEntity; + + // The two fields checked can be passed. These are the defaults + private _fromId = "requestingAccountId"; + private _toId = "targetAccountId"; + + // Set to 'true' if the filtering was passed in the criteria query parameters + private _doingQuery = false; + + // Create a new filter and specify the requesting account and + // field in target entity to check for match. + // "accountId" works for most things but scoping domains by their owners + // requires checking "sponsorAccountId"> + public constructor(pRequestorAccount: AccountEntity, + pFrom = "requestingAccountId", pTo = "targetAccountId") { + super(); + this._accessingAcct = pRequestorAccount; + this._fromId = pFrom; + this._toId = pTo; + + } + + // Process query parameters + public parametersFromRequest(pRequest: Request): void { + try { + if (pRequest.query.hasOwnProperty("asAdmin")) { + const askingForAdmin = pRequest.query.asAdmin === "true"; + if (askingForAdmin) { + if (Accounts.isAdmin(this._accessingAcct)) { + this._asAdmin = true; + } + } + } + // The administrator can specify an account to limit requests to + if (pRequest.query.acct && typeof pRequest.query.acct === "string") { + this._targetAcct = pRequest.query.account as string; + } + Logger.cdebug("query-detail", `RequestScopeFilter.parametersFromRequest: asAdmin=${this._asAdmin}, target=${this._targetAcct}`); + } catch (e) { + Logger.error("RequestScopeFilter: parameters from request: exception: " + e); + } + } + + // Add any parameters to the response + public addResponseFields(pRequest: Request) { + + } + + // Return if we've found admin enabling parameters + public AsAdmin(): boolean { + return this._asAdmin; + } + + public criteriaTest(pToTest: any): boolean { + if (!this._doingQuery) { + if (pToTest.hasOwnProperty(this._fromId) && pToTest.hasOwnProperty(this._toId)) { + let getAccountId = this._accessingAcct.id; + if (this._asAdmin) { + if (typeof this._targetAcct !== "undefined") { + getAccountId = this._targetAcct; + } else { + return true; // we're admin so the entry matches + } + } + return pToTest[this._fromId] === getAccountId || pToTest[this._toId] === getAccountId; + } + } + return true; } - catch (e) { - Logger.error('RequestScopeFilter: parameters from request: exception: ' + e); - }; - }; - - // Add any parameters to the response - public addResponseFields(pRequest: Request) { - return; - }; - - // Return if we've found admin enabling parameters - public AsAdmin(): boolean { - return this._asAdmin; - }; - - public criteriaTest(pToTest: any): boolean { - if (! this._doingQuery) { - if (pToTest.hasOwnProperty(this._fromId) && pToTest.hasOwnProperty(this._toId)) { - let getAccountId = this._accessingAcct.id; - if (this._asAdmin) { - if (typeof(this._targetAcct) !== 'undefined') { - getAccountId = this._targetAcct - } - else { - return true; // we're admin so the entry matches - }; - }; - return pToTest[this._fromId] === getAccountId || pToTest[this._toId] === getAccountId; - }; - }; - return true; - }; public async criteriaTestAsync(pThingy: any): Promise { return this.criteriaTest(pThingy); - }; - - public criteriaParameters(): any { - this._doingQuery = true; - let getAccountId = this._accessingAcct.id; - if (this._asAdmin && typeof(this._targetAcct) !== 'undefined') { - getAccountId = this._targetAcct - }; - const criteria: any = {}; - // If not an admin, the found items must match the id of the requestor - if (! this._asAdmin) { - criteria.$or = [ SimpleObject( this._fromId, getAccountId), - SimpleObject(this._toId , getAccountId) ]; - }; - return criteria; - }; - - public sortCriteriaParameters(): any { - return undefined; - }; -}; + } + + public criteriaParameters(): any { + this._doingQuery = true; + let getAccountId = this._accessingAcct.id; + if (this._asAdmin && typeof this._targetAcct !== "undefined") { + getAccountId = this._targetAcct; + } + const criteria: any = {}; + // If not an admin, the found items must match the id of the requestor + if (!this._asAdmin) { + criteria.$or = [ + SimpleObject(this._fromId, getAccountId), + SimpleObject(this._toId, getAccountId) + ]; + } + return criteria; + } + + public sortCriteriaParameters(): any { + return undefined; + } +} diff --git a/src/Entities/EntityFilters/VisibilityFilter.ts b/src/Entities/EntityFilters/VisibilityFilter.ts index 5f7b0742..14904a7c 100644 --- a/src/Entities/EntityFilters/VisibilityFilter.ts +++ b/src/Entities/EntityFilters/VisibilityFilter.ts @@ -1,150 +1,147 @@ -// Copyright 2021 Vircadia Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -'use strict' - -import { Request } from 'express'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { Visibility } from '@Entities/Sets/Visibility'; -import { Accounts } from '@Entities/Accounts'; -import { Domains } from '@Entities/Domains'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { Logger } from '@Tools/Logging'; -import { DomainEntity } from '@Entities/DomainEntity'; - -// A filter to return entities the requestor can "see". -// This checks for 'friend', 'connection' and 'asAdmin' relationships -// and returns entities that are friends, etc of the requestor. -// -// NOTE NOTE NOTE NOTE NOTE -// This filter can't be used as a usual criteria filter because the test -// requires a DB $LOOKUP operation (to lookup the account of the Place's domain) -// and this function can't be done until the DB queries use the aggregate() operation. -// When the DB queries are changed, this can be reworked into a DB filter. -// Until then, use the criteriaTestAsync inline to filter. -// NOTE NOTE NOTE NOTE NOTE -// Filter entities that have a 'visibility' field. -// Visibility of such entities depends on the requestor. -// An admin account can make the request 'asAdmin' to see all Places -// &asAdmin=true -export class VisibilityFilter extends CriteriaFilter { - private _asAdmin: boolean = false; - private _accessingAcct: AccountEntity; - private _accessingAcctName: string; - private _friends: string[] = []; - private _connections: string[] = []; - - // Set to 'true' if the pagination was passed in the criteria query parameters - private _doingQuery: boolean = false; - - // Note that requesting account can be 'undefined' - public constructor(pRequestorAccount: AccountEntity) { - super(); - this._accessingAcct = pRequestorAccount; - return; - } - - public parametersFromRequest(pRequest: Request) : void { - try { - if (this._accessingAcct) { - this._accessingAcctName = this._accessingAcct.username; - if (this._accessingAcct.hasOwnProperty('friends')) { - this._friends = this._accessingAcct.friends ?? []; - }; - if (this._accessingAcct.hasOwnProperty('connections')) { - this._connections = this._accessingAcct.connections ?? []; - }; - if (pRequest.query.hasOwnProperty('asAdmin')) { - const askingForAdmin = pRequest.query.asAdmin === 'true'; - if (askingForAdmin) { - if (Accounts.isAdmin(this._accessingAcct)) { - this._asAdmin = true; - }; - }; - }; - }; - Logger.cdebug('query-detail', `VisibilityFilter.parametersFromRequest: asAdmin=${this._asAdmin}`); - } - catch (e) { - Logger.error('VisibilityFilter: parameters from request: exception: ' + e); - }; - }; - - // Add any parameters to the response - public addResponseFields(pRequest: Request) { - return; - }; - - // Return if we've found admin enabling parameters - public AsAdmin(): boolean { - return this._asAdmin; - }; - - public criteriaTest(pToTest: any): boolean { - let ret = this._doingQuery || this._asAdmin; - if (! ret) { - if (pToTest.hasOwnProperty('visibility')) { - ret = pToTest.visibility === Visibility.OPEN - } - else { - ret = true; - }; - } - return ret; - }; - - public async criteriaTestAsync(pToTest: any, pDomain?: DomainEntity): Promise { - let ret = this._doingQuery || this._asAdmin; - if (! ret) { - if (pToTest.hasOwnProperty('visibility')) { - switch (pToTest.visibility) { - case Visibility.OPEN: - ret = true; - break; - case Visibility.PRIVATE: - if (this._accessingAcct && pToTest.hasOwnProperty('domainId')) { - const aDomain = pDomain ?? await Domains.getDomainWithId(pToTest.domainId); - if (aDomain) { - ret = aDomain.sponsorAccountId === this._accessingAcct.id; - }; - }; - break; - default: - ret = false; - break; - } - } - else { - // if 'visibility' is not specified, it's assumed "OPEN" - ret = true; - }; - } - return ret; - }; - - public criteriaParameters(): any { - this._doingQuery = true; - const criteria: any = {}; - if (! this._asAdmin) { - /* tslint:disable-next-line */ - criteria['$or'] = [ { 'visibility': { '$exists': false }}, - { 'visibility': Visibility.OPEN }, - ]; - } - return criteria; - }; - - public sortCriteriaParameters(): any { - return null; - }; -}; +// Copyright 2021 Vircadia Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import { Request } from "express"; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { Visibility } from "@Entities/Sets/Visibility"; +import { Accounts } from "@Entities/Accounts"; +import { Domains } from "@Entities/Domains"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { Logger } from "@Tools/Logging"; +import { DomainEntity } from "@Entities/DomainEntity"; + +// A filter to return entities the requestor can "see". +// This checks for 'friend', 'connection' and 'asAdmin' relationships +// and returns entities that are friends, etc of the requestor. +// +// NOTE NOTE NOTE NOTE NOTE +// This filter can't be used as a usual criteria filter because the test +// requires a DB $LOOKUP operation (to lookup the account of the Place's domain) +// and this function can't be done until the DB queries use the aggregate() operation. +// When the DB queries are changed, this can be reworked into a DB filter. +// Until then, use the criteriaTestAsync inline to filter. +// NOTE NOTE NOTE NOTE NOTE +// Filter entities that have a 'visibility' field. +// Visibility of such entities depends on the requestor. +// An admin account can make the request 'asAdmin' to see all Places +// &asAdmin=true +export class VisibilityFilter extends CriteriaFilter { + private _asAdmin = false; + private _accessingAcct: AccountEntity; + private _accessingAcctName: string; + private _friends: string[] = []; + private _connections: string[] = []; + + // Set to 'true' if the pagination was passed in the criteria query parameters + private _doingQuery = false; + + // Note that requesting account can be 'undefined' + public constructor(pRequestorAccount: AccountEntity) { + super(); + this._accessingAcct = pRequestorAccount; + + } + + public parametersFromRequest(pRequest: Request): void { + try { + if (this._accessingAcct) { + this._accessingAcctName = this._accessingAcct.username; + if (this._accessingAcct.hasOwnProperty("friends")) { + this._friends = this._accessingAcct.friends ?? []; + } + if (this._accessingAcct.hasOwnProperty("connections")) { + this._connections = this._accessingAcct.connections ?? []; + } + if (pRequest.query.hasOwnProperty("asAdmin")) { + const askingForAdmin = pRequest.query.asAdmin === "true"; + if (askingForAdmin) { + if (Accounts.isAdmin(this._accessingAcct)) { + this._asAdmin = true; + } + } + } + } + Logger.cdebug("query-detail", `VisibilityFilter.parametersFromRequest: asAdmin=${this._asAdmin}`); + } catch (e) { + Logger.error("VisibilityFilter: parameters from request: exception: " + e); + } + } + + // Add any parameters to the response + public addResponseFields(pRequest: Request) { + + } + + // Return if we've found admin enabling parameters + public AsAdmin(): boolean { + return this._asAdmin; + } + + public criteriaTest(pToTest: any): boolean { + let ret = this._doingQuery || this._asAdmin; + if (!ret) { + if (pToTest.hasOwnProperty("visibility")) { + ret = pToTest.visibility === Visibility.OPEN; + } else { + ret = true; + } + } + return ret; + } + + public async criteriaTestAsync(pToTest: any, pDomain?: DomainEntity): Promise { + let ret = this._doingQuery || this._asAdmin; + if (!ret) { + if (pToTest.hasOwnProperty("visibility")) { + switch (pToTest.visibility) { + case Visibility.OPEN: + ret = true; + break; + case Visibility.PRIVATE: + if (this._accessingAcct && pToTest.hasOwnProperty("domainId")) { + const aDomain = pDomain ?? await Domains.getDomainWithId(pToTest.domainId); + if (aDomain) { + ret = aDomain.sponsorAccountId === this._accessingAcct.id; + } + } + break; + default: + ret = false; + break; + } + } else { + // if 'visibility' is not specified, it's assumed "OPEN" + ret = true; + } + } + return ret; + } + + public criteriaParameters(): any { + this._doingQuery = true; + const criteria: any = {}; + if (!this._asAdmin) { + // eslint-disable-next-line + criteria['$or'] = [ { 'visibility': { '$exists': false }}, + { "visibility": Visibility.OPEN } + ]; + } + return criteria; + } + + public sortCriteriaParameters(): any { + return null; + } +} diff --git a/src/Entities/PlaceEntity.ts b/src/Entities/PlaceEntity.ts index 8f025207..e5499429 100755 --- a/src/Entities/PlaceEntity.ts +++ b/src/Entities/PlaceEntity.ts @@ -11,11 +11,11 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; -import { Entity } from '@Entities/Entity'; +import Config from "@Base/config"; + +import { Entity } from "@Entities/Entity"; // NOTE: this class cannot have functions in them as they are just JSON to and from the database export class PlaceEntity implements Entity { @@ -34,15 +34,15 @@ export class PlaceEntity implements Entity { // A Place can have a beacon that updates current state and information // If current information is not supplied, attendance defaults to domain's - public currentAttendance: number // current attendance at the Place - public currentImages: string[] // images at the session - public currentInfo: string // JSON information about the session - public currentLastUpdateTime: Date // time that the last session information was updated - public currentAPIKeyTokenId: string // API key for updating the session information + public currentAttendance: number; // current attendance at the Place + public currentImages: string[]; // images at the session + public currentInfo: string; // JSON information about the session + public currentLastUpdateTime: Date; // time that the last session information was updated + public currentAPIKeyTokenId: string; // API key for updating the session information // admin stuff public iPAddrOfFirstContact: string; // IP address that registered this place public whenCreated: Date; // What the variable name says // 'lastActivity' is computed by Places.initPlaces and used for aliveness checks public lastActivity: Date; // newest of currentLastUpdateTime and Domain.timeOfLastHeartbeat -}; \ No newline at end of file +} diff --git a/src/Entities/PlaceFields.ts b/src/Entities/PlaceFields.ts index 3f363879..cb653fba 100644 --- a/src/Entities/PlaceFields.ts +++ b/src/Entities/PlaceFields.ts @@ -11,291 +11,283 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; -import { Entity } from '@Entities/Entity'; -import { PlaceEntity } from '@Entities/PlaceEntity'; +import Config from "@Base/config"; -import { Domains } from '@Entities/Domains'; -import { Places } from '@Entities/Places'; -import { Accounts } from '@Entities/Accounts'; -import { AuthToken } from '@Entities/AuthToken'; -import { Maturity } from '@Entities/Sets/Maturity'; -import { Visibility } from '@Entities/Sets/Visibility'; +import { Entity } from "@Entities/Entity"; +import { PlaceEntity } from "@Entities/PlaceEntity"; -import { Perm } from '@Route-Tools/Perm'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; +import { Domains } from "@Entities/Domains"; +import { Places } from "@Entities/Places"; +import { Accounts } from "@Entities/Accounts"; +import { AuthToken } from "@Entities/AuthToken"; +import { Maturity } from "@Entities/Sets/Maturity"; +import { Visibility } from "@Entities/Sets/Visibility"; -import { FieldDefn, ValidateResponse } from '@Route-Tools/EntityFieldDefn'; -import { isStringValidator, isSArraySet, isPathValidator, isLongPathValidator, isDateValidator, isObjectValidator, isNumberValidator } from '@Route-Tools/Validators'; -import { simpleGetter, simpleSetter, noSetter, sArraySetter, dateStringGetter, verifyAllSArraySetValues} from '@Route-Tools/Validators'; +import { Perm } from "@Route-Tools/Perm"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; +import { FieldDefn, ValidateResponse } from "@Route-Tools/EntityFieldDefn"; +import { isStringValidator, isSArraySet, isPathValidator, isLongPathValidator, isDateValidator, isObjectValidator, isNumberValidator } from "@Route-Tools/Validators"; +import { simpleGetter, simpleSetter, noSetter, sArraySetter, dateStringGetter, verifyAllSArraySetValues } from "@Route-Tools/Validators"; -import { Logger } from '@Tools/Logging'; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; + +import { Logger } from "@Tools/Logging"; // Naming and access for the fields in a PlaceEntity. // Indexed by request_field_name. export const placeFields: { [key: string]: FieldDefn } = { - 'id': { - entity_field: 'id', - request_field_name: 'id', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "id": { + entity_field: "id", + request_field_name: "id", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'name': { - entity_field: 'name', - request_field_name: 'name', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAIN, Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "name": { + entity_field: "name", + request_field_name: "name", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAIN, Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { // Verify that the placename is unique let validity: ValidateResponse; - if (typeof(pVal) === 'string' && pVal.length > 0) { - if (pVal.length <= Config['metaverse-server']['max-name-length']) { - if( /^[A-Za-z][A-Za-z0-9\-]*$/.test(pVal) ) { + if (typeof pVal === "string" && pVal.length > 0) { + if (pVal.length <= Config["metaverse-server"]["max-name-length"]) { + if ((/^[A-Za-z][A-Za-z0-9\-]*$/).test(pVal)) { const maybePlace = await Places.getPlaceWithName(pVal); // If no other place with this name or we're setting our own name if (IsNullOrEmpty(maybePlace) || (pEntity as PlaceEntity).id === maybePlace.id) { validity = { valid: true }; + } else { + validity = { valid: false, reason: "place name already exists" }; } - else { - validity = { valid: false, reason: 'place name already exists' }; - }; + } else { + validity = { valid: false, reason: "place name characters must be A-Za-z0-9-." }; } - else { - validity = { valid: false, reason: 'place name characters must be A-Za-z0-9-.'}; - }; + } else { + validity = { valid: false, reason: "place name too long" }; } - else { - validity = { valid: false, reason: 'place name too long' }; - }; + } else { + validity = { valid: false, reason: "place name must be a string" }; } - else { - validity = { valid: false, reason: 'place name must be a string' }; - }; return validity; }, setter: simpleSetter, getter: simpleGetter }, - 'displayName': { - entity_field: 'displayName', - request_field_name: 'displayName', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "displayName": { + entity_field: "displayName", + request_field_name: "displayName", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'description': { - entity_field: 'description', - request_field_name: 'description', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "description": { + entity_field: "description", + request_field_name: "description", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'visibility': { - entity_field: 'visibility', - request_field_name: 'visibility', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "visibility": { + entity_field: "visibility", + request_field_name: "visibility", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - if(typeof(pVal) === 'string' && Visibility.KnownVisibility(pVal)) { + if (typeof pVal === "string" && Visibility.KnownVisibility(pVal)) { return { valid: true }; } - return { valid: false, reason: 'not accepted visibility value'}; + return { valid: false, reason: "not accepted visibility value" }; }, setter: simpleSetter, getter: simpleGetter }, - 'domainId': { - entity_field: 'domainId', - request_field_name: 'domainId', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.ADMIN ], + "domainId": { + entity_field: "domainId", + request_field_name: "domainId", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any, pAuth: AuthToken): Promise => { // This is setting a place to a new domainId. Make sure the domain exists // and requestor has access to that domain. - let validity: ValidateResponse = { valid: false, reason: 'system error' }; - if (typeof(pVal) === 'string') { + let validity: ValidateResponse = { valid: false, reason: "system error" }; + if (typeof pVal === "string") { const maybeDomain = await Domains.getDomainWithId(pVal); if (IsNotNullOrEmpty(maybeDomain)) { if (IsNotNullOrEmpty(pAuth)) { - if (await checkAccessToEntity(pAuth, maybeDomain, [ Perm.SPONSOR, Perm.ADMIN ])) { + if (await checkAccessToEntity(pAuth, maybeDomain, [Perm.SPONSOR, Perm.ADMIN])) { validity = { valid: true }; - } - else { + } else { Logger.error(`PlaceEntity:domainId.validate: attempt to set to non-owned domain. RequesterAId=${pAuth.accountId}, DomainId=${pVal}`); - validity = { valid: false, reason: 'not authorized to change domainId' }; - }; - }; - } - else { + validity = { valid: false, reason: "not authorized to change domainId" }; + } + } + } else { Logger.error(`PlaceEntity:domainId.validate: attempt to set to non-existant domain. RequesterAId=${pAuth.accountId}, DomainId=${pVal}`); - validity = { valid: false, reason: 'domain not found' }; - }; + validity = { valid: false, reason: "domain not found" }; + } + } else { + validity = { valid: false, reason: "invalid domainId format" }; } - else { - validity = { valid: false, reason: 'invalid domainId format' }; - }; return validity; }, setter: simpleSetter, getter: simpleGetter }, - 'address': { // The network address of a location in the domain - entity_field: 'path', - request_field_name: 'address', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "address": { // The network address of a location in the domain + entity_field: "path", + request_field_name: "address", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isLongPathValidator, setter: simpleSetter, getter: async (pField: FieldDefn, pEntity: Entity): Promise => { - return await Places.getAddressString(pEntity as PlaceEntity); + return Places.getAddressString(pEntity as PlaceEntity); } }, - 'path': { // the address within the domain - entity_field: 'path', - request_field_name: 'path', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "path": { // the address within the domain + entity_field: "path", + request_field_name: "path", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isPathValidator, setter: simpleSetter, getter: simpleGetter }, - 'maturity': { - entity_field: 'maturity', - request_field_name: 'maturity', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "maturity": { + entity_field: "maturity", + request_field_name: "maturity", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - if(typeof(pVal) === 'string' && Maturity.KnownMaturity(pVal)) { + if (typeof pVal === "string" && Maturity.KnownMaturity(pVal)) { return { valid: true }; } - return { valid: false, reason: 'not accepted maturity value'}; + return { valid: false, reason: "not accepted maturity value" }; }, setter: simpleSetter, getter: simpleGetter }, - 'tags': { - entity_field: 'tags', - request_field_name: 'tags', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "tags": { + entity_field: "tags", + request_field_name: "tags", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isSArraySet, setter: sArraySetter, getter: simpleGetter }, - 'managers': { - entity_field: 'managers', - request_field_name: 'managers', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.ADMIN ], + "managers": { + entity_field: "managers", + request_field_name: "managers", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.ADMIN], validate: async (pField: FieldDefn, pEntity: Entity, pVal: any): Promise => { - if (await verifyAllSArraySetValues(pVal, async ( name: string ) => { + if (await verifyAllSArraySetValues(pVal, async (name: string) => { return IsNotNullOrEmpty(await Accounts.getAccountWithUsername(name)); - } ) ) { + })) { return { valid: true }; } - return { valid: false, reason: 'unknown user names'}; + return { valid: false, reason: "unknown user names" }; }, setter: sArraySetter, getter: simpleGetter }, - 'thumbnail': { - entity_field: 'thumbnail', - request_field_name: 'thumbnail', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "thumbnail": { + entity_field: "thumbnail", + request_field_name: "thumbnail", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'images': { - entity_field: 'images', - request_field_name: 'images', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "images": { + entity_field: "images", + request_field_name: "images", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isSArraySet, setter: sArraySetter, getter: simpleGetter }, - 'current_attendance': { - entity_field: 'currentAttendance', - request_field_name: 'current_attendance', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "current_attendance": { + entity_field: "currentAttendance", + request_field_name: "current_attendance", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isNumberValidator, setter: simpleSetter, getter: simpleGetter }, - 'current_images': { - entity_field: 'currentImages', - request_field_name: 'current_images', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "current_images": { + entity_field: "currentImages", + request_field_name: "current_images", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isSArraySet, setter: sArraySetter, getter: simpleGetter }, - 'current_info': { - entity_field: 'currentInfo', - request_field_name: 'current_info', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN ], + "current_info": { + entity_field: "currentInfo", + request_field_name: "current_info", + get_permissions: [Perm.ALL], + set_permissions: [Perm.DOMAINACCESS, Perm.MANAGER, Perm.ADMIN], validate: isObjectValidator, setter: simpleSetter, getter: simpleGetter }, - 'current_last_update_time': { - entity_field: 'currentLastUpdateTime', - request_field_name: 'current_last_update_time', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "current_last_update_time": { + entity_field: "currentLastUpdateTime", + request_field_name: "current_last_update_time", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isDateValidator, setter: simpleSetter, getter: dateStringGetter }, - 'current_api_key': { - entity_field: 'currentAPIKeyTokenId', - request_field_name: 'current_api_key', - get_permissions: [ Perm.DOMAINACCESS, Perm.ADMIN], - set_permissions: [ Perm.NONE ], + "current_api_key": { + entity_field: "currentAPIKeyTokenId", + request_field_name: "current_api_key", + get_permissions: [Perm.DOMAINACCESS, Perm.ADMIN], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: noSetter, getter: async (pField: FieldDefn, pEntity: Entity): Promise => { - if (pEntity.hasOwnProperty('currentAPIKeyTokenId')) { + if (pEntity.hasOwnProperty("currentAPIKeyTokenId")) { return Places.getCurrentInfoAPIKey(pEntity as PlaceEntity); - }; - return 'unknown'; + } + return "unknown"; } }, // admin stuff - 'addr_of_first_contact': { - entity_field: 'iPAddrOfFirstContact', - request_field_name: 'addr_of_first_contact', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "addr_of_first_contact": { + entity_field: "iPAddrOfFirstContact", + request_field_name: "addr_of_first_contact", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isStringValidator, setter: simpleSetter, getter: simpleGetter }, - 'when_place_entry_created': { - entity_field: 'whenCreated', - request_field_name: 'when_place_entry_created', - get_permissions: [ Perm.ALL ], - set_permissions: [ Perm.NONE ], + "when_place_entry_created": { + entity_field: "whenCreated", + request_field_name: "when_place_entry_created", + get_permissions: [Perm.ALL], + set_permissions: [Perm.NONE], validate: isDateValidator, setter: noSetter, getter: dateStringGetter } }; - diff --git a/src/Entities/Places.ts b/src/Entities/Places.ts index 170485b6..287d6e22 100755 --- a/src/Entities/Places.ts +++ b/src/Entities/Places.ts @@ -11,35 +11,35 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; -import { Domains } from '@Entities/Domains'; +import Config from "@Base/config"; -import { AccountEntity } from '@Entities/AccountEntity'; -import { PlaceEntity } from '@Entities/PlaceEntity'; -import { placeFields } from '@Entities/PlaceFields'; -import { DomainEntity } from './DomainEntity'; +import { Domains } from "@Entities/Domains"; -import { AuthToken } from '@Entities/AuthToken'; -import { Tokens, TokenScope } from '@Entities/Tokens'; +import { AccountEntity } from "@Entities/AccountEntity"; +import { PlaceEntity } from "@Entities/PlaceEntity"; +import { placeFields } from "@Entities/PlaceFields"; +import { DomainEntity } from "./DomainEntity"; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { AuthToken } from "@Entities/AuthToken"; +import { Tokens, TokenScope } from "@Entities/Tokens"; -import { ValidateResponse } from '@Route-Tools/EntityFieldDefn'; -import { getEntityField, setEntityField, getEntityUpdateForField } from '@Route-Tools/GetterSetter'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; -import { createObject, getObject, getObjects, updateObjectFields, deleteOne, deleteMany, noCaseCollation } from '@Tools/Db'; +import { ValidateResponse } from "@Route-Tools/EntityFieldDefn"; +import { getEntityField, setEntityField, getEntityUpdateForField } from "@Route-Tools/GetterSetter"; -import { GenUUID, IsNullOrEmpty, IsNotNullOrEmpty, genRandomString } from '@Tools/Misc'; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; -import { PlaceFilterInfo } from './EntityFilters/PlaceFilterInfo'; -import { Accounts } from './Accounts'; +import { createObject, getObject, getObjects, updateObjectFields, deleteOne, deleteMany, noCaseCollation } from "@Tools/Db"; -export let placeCollection = 'places'; +import { GenUUID, IsNullOrEmpty, IsNotNullOrEmpty, genRandomString } from "@Tools/Misc"; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; +import { PlaceFilterInfo } from "./EntityFilters/PlaceFilterInfo"; +import { Accounts } from "./Accounts"; + +export const placeCollection = "places"; // Initialize place management. export function initPlaces(): void { @@ -49,7 +49,7 @@ export function initPlaces(): void { // Update current attendance and aliveness for places // This saves reading the domain information on every place fetch // [Note: There is a lot of checking for values changing to reduce database updates] - setInterval( async () => { + setInterval(async () => { let numPlaces = 0; let numUnhookedPlaces = 0; @@ -59,7 +59,7 @@ export function initPlaces(): void { const inactivePlaceTime = Places.dateWhenNotActive(); // The date when a Place's "current" update information is considered stale const lastGoodUpdateTime = new Date(Date.now() - - (Config['metaverse-server']['place-current-timeout-minutes'] * 60 * 1000)); + - Config["metaverse-server"]["place-current-timeout-minutes"] * 60 * 1000); // Logger.debug(`PlaceActivity: inactive=${inactivePlaceTime.toISOString()}, stale=${lastGoodUpdateTime.toISOString()}`); for await (const aPlace of Places.enumerateAsync(placer)) { @@ -77,11 +77,10 @@ export function initPlaces(): void { if (IsNullOrEmpty(aPlace.currentAttendance) || aPlace.currentAttendance !== domainAttendance) { aPlace.currentAttendance = domainAttendance; updates.currentAttendance = aPlace.currentAttendance; - }; + } aPlace.lastActivity = aDomain.timeOfLastHeartbeat ?? inactivePlaceTime; updates.lastActivity = aPlace.lastActivity; - } - else { + } else { // can't find the domain to go with the place. Set everything to zero and update DB // Logger.debug(` PlaceActivity: no domain found for place ${aPlace.name}`); numUnhookedPlaces++; @@ -89,49 +88,52 @@ export function initPlaces(): void { aPlace.lastActivity = inactivePlaceTime; updates.currentAttendance = aPlace.currentAttendance; updates.lastActivity = aPlace.lastActivity; - }; - } - else { + } + } else { // The Place has updated current info so just set the current activity time // Logger.debug(`PlaceActivity: place ${aPlace.name} has currentLastUpdateTime so updating activity`); if (IsNullOrEmpty(aPlace.lastActivity) || aPlace.lastActivity !== aPlace.currentLastUpdateTime) { aPlace.lastActivity = aPlace.currentLastUpdateTime; updates.lastActivity = aPlace.lastActivity; - }; - }; + } + } if (aPlace.lastActivity <= inactivePlaceTime) { // Logger.debug(` PlaceActivity: place ${aPlace.name} deemed inactive`); numInactivePlaces++; - }; + } // This updateEntityFields does nothing if 'updates' is empty await Places.updateEntityFields(aPlace, updates); - }; + } // Logger.debug(`PlaceActivity: numPlaces=${numPlaces}, unhookedPlaces=${numUnhookedPlaces}, inactivePlaces=${numInactivePlaces}`); - }, 1000 * Config['metaverse-server']['place-check-last-activity-seconds'] ); -}; + }, 1000 * Config["metaverse-server"]["place-check-last-activity-seconds"]); +} export const Places = { async getPlaceWithId(pPlaceId: string): Promise { - return IsNullOrEmpty(pPlaceId) ? null : getObject(placeCollection, - new GenericFilter({ 'id': pPlaceId })); + return IsNullOrEmpty(pPlaceId) + ? null + : getObject(placeCollection, + new GenericFilter({ "id": pPlaceId })); }, async getPlaceWithName(pPlacename: string): Promise { - return IsNullOrEmpty(pPlacename) ? null : getObject(placeCollection, - new GenericFilter({ 'name': pPlacename }), - noCaseCollation); + return IsNullOrEmpty(pPlacename) + ? null + : getObject(placeCollection, + new GenericFilter({ "name": pPlacename }), + noCaseCollation); }, - async addPlace(pPlaceEntity: PlaceEntity) : Promise { + async addPlace(pPlaceEntity: PlaceEntity): Promise { Logger.info(`Places: creating place ${pPlaceEntity.name}, id=${pPlaceEntity.id}`); return IsNullOrEmpty(pPlaceEntity) ? null : createObject(placeCollection, pPlaceEntity); }, async createPlace(pAccountId: string): Promise { const newPlace = new PlaceEntity(); newPlace.id = GenUUID(); - newPlace.name = 'UNKNOWN-' + genRandomString(5); - newPlace.path = '/0,0,0/0,0,0,1'; + newPlace.name = "UNKNOWN-" + genRandomString(5); + newPlace.path = "/0,0,0/0,0,0,1"; newPlace.whenCreated = new Date(); newPlace.currentAttendance = 0; - const APItoken = await Tokens.createToken(pAccountId, [ TokenScope.PLACE ], -1); + const APItoken = await Tokens.createToken(pAccountId, [TokenScope.PLACE], -1); await Tokens.addToken(APItoken); // put token into DB newPlace.currentAPIKeyTokenId = APItoken.id; return newPlace; @@ -141,9 +143,9 @@ export const Places = { let newPlacename = pPlaceName; const existingPlace = await Places.getPlaceWithName(newPlacename); if (existingPlace) { - newPlacename = newPlacename + '-' + genRandomString(5); + newPlacename = newPlacename + "-" + genRandomString(5); Logger.info(`uniqifyPlaceName: non-unique place name ${pPlaceName}. Creating ${newPlacename}`); - }; + } return newPlacename; }, // Get the value of a place field with the fieldname. @@ -151,27 +153,27 @@ export const Places = { // Returns the value. Could be 'undefined' whether the requestor doesn't have permissions or that's // the actual field value. async getField(pAuthToken: AuthToken, pPlace: PlaceEntity, - pField: string, pRequestingAccount?: AccountEntity): Promise { + pField: string, pRequestingAccount?: AccountEntity): Promise { return getEntityField(placeFields, pAuthToken, pPlace, pField, pRequestingAccount); }, // Set a place field with the fieldname and a value. // Checks to make sure the setter has permission to set. // Returns 'true' if the value was set and 'false' if the value could not be set. async setField(pAuthToken: AuthToken, // authorization for making this change - pPlace: PlaceEntity, // the place being changed - pField: string, pVal: any, // field being changed and the new value - pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known - pUpdates?: VKeyedCollection // where to record updates made (optional) - ): Promise { + pPlace: PlaceEntity, // the place being changed + pField: string, pVal: any, // field being changed and the new value + pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known + pUpdates?: VKeyedCollection // where to record updates made (optional) + ): Promise { return setEntityField(placeFields, pAuthToken, pPlace, pField, pVal, pRequestingAccount, pUpdates); }, // Verify that the passed value is legal for the named field async validateFieldValue(pFieldName: string, pValue: any): Promise { const defn = placeFields[pFieldName]; if (defn) { - return await defn.validate(defn, defn.request_field_name, pValue); - }; - return { 'valid': false, 'reason': 'Unknown field name' }; + return defn.validate(defn, defn.request_field_name, pValue); + } + return { "valid": false, "reason": "Unknown field name" }; }, // Generate an 'update' block for the specified field or fields. // This is a field/value collection that can be passed to the database routines. @@ -179,34 +181,34 @@ export const Places = { // we want the actual value (whatever it is) to go into the database. // If an existing VKeyedCollection is passed, it is added to an returned. getUpdateForField(pPlace: PlaceEntity, - pField: string | string[], pExisting?: VKeyedCollection): VKeyedCollection { + pField: string | string[], pExisting?: VKeyedCollection): VKeyedCollection { return getEntityUpdateForField(placeFields, pPlace, pField, pExisting); }, - async removePlace(pPlaceEntity: PlaceEntity) : Promise { + async removePlace(pPlaceEntity: PlaceEntity): Promise { Logger.info(`Places: removing place ${pPlaceEntity.name}, id=${pPlaceEntity.id}`); - return deleteOne(placeCollection, new GenericFilter({ 'id': pPlaceEntity.id }) ); + return deleteOne(placeCollection, new GenericFilter({ "id": pPlaceEntity.id })); }, - async removeMany(pCriteria: CriteriaFilter) : Promise { + async removeMany(pCriteria: CriteriaFilter): Promise { return deleteMany(placeCollection, pCriteria); }, async *enumerateAsync(pPager: CriteriaFilter, - pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { + pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { for await (const place of getObjects(placeCollection, pPager, pInfoer, pScoper)) { yield place; - }; + } // return getObjects(placeCollection, pCriteria, pPager); // not sure why this doesn't work }, // The contents of this entity have been updated async updateEntityFields(pEntity: PlaceEntity, pFields: VKeyedCollection): Promise { return updateObjectFields(placeCollection, - new GenericFilter({ 'id': pEntity.id }), pFields); + new GenericFilter({ "id": pEntity.id }), pFields); }, - dateWhenNotActive() : Date { - return new Date(Date.now() - (Config['metaverse-server']['place-inactive-timeout-minutes'] * 60 * 1000)); + dateWhenNotActive(): Date { + return new Date(Date.now() - Config["metaverse-server"]["place-inactive-timeout-minutes"] * 60 * 1000); }, async getCurrentInfoAPIKey(pPlace: PlaceEntity): Promise { @@ -215,7 +217,7 @@ export const Places = { const keyToken = await Tokens.getTokenWithTokenId(pPlace.currentAPIKeyTokenId); if (IsNotNullOrEmpty(keyToken)) { key = keyToken.token; - }; + } return key; }, @@ -223,10 +225,10 @@ export const Places = { // Compute and return the string for the Places's address. // The address is of the form "optional-domain/x,y,z/x,y,z,w". // If the domain is missing, the domain-server's network address is added - let addr = pPlace.path ?? '/0,0,0/0,0,0,1'; + let addr = pPlace.path ?? "/0,0,0/0,0,0,1"; // If no domain/address specified in path, build addr using reported domain IP/port - const pieces = addr.split('/'); + const pieces = addr.split("/"); if (pieces[0].length === 0) { const aDomain = await Domains.getDomainWithId(pPlace.domainId); if (IsNotNullOrEmpty(aDomain)) { @@ -234,11 +236,11 @@ export const Places = { let domainAddr = aDomain.networkAddr; if (IsNotNullOrEmpty(aDomain.networkPort)) { domainAddr = aDomain.networkAddr + ":" + aDomain.networkPort; - }; + } addr = domainAddr + addr; - }; - }; - }; + } + } + } return addr; }, @@ -252,10 +254,10 @@ export const Places = { if (aDomain) { const aAccount = await Accounts.getAccountWithId(aDomain.sponsorAccountId); if (aAccount) { - pPlace.managers = [ aAccount.username ]; - }; - }; - await Places.updateEntityFields(pPlace, { 'managers': pPlace.managers }) + pPlace.managers = [aAccount.username]; + } + } + await Places.updateEntityFields(pPlace, { "managers": pPlace.managers }); } return pPlace.managers; } diff --git a/src/Entities/RequestEntity.ts b/src/Entities/RequestEntity.ts index c9d09987..d26f5968 100755 --- a/src/Entities/RequestEntity.ts +++ b/src/Entities/RequestEntity.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' import { IsNotNullOrEmpty } from "@Tools/Misc"; @@ -39,5 +38,4 @@ export class RequestEntity { // requestType == VERIFYEMAIL // 'requestingAccountId' is the account being verified public verificationCode: string; // the code we're waiting for -}; - +} diff --git a/src/Entities/Requests.ts b/src/Entities/Requests.ts index 573ca098..4685fd70 100755 --- a/src/Entities/Requests.ts +++ b/src/Entities/Requests.ts @@ -11,80 +11,85 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { RequestEntity } from '@Entities/RequestEntity'; +import { Config } from "@Base/config"; -import { createObject, getObject, getObjects, updateObjectFields, deleteOne, deleteMany } from '@Tools/Db'; +import { RequestEntity } from "@Entities/RequestEntity"; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { createObject, getObject, getObjects, updateObjectFields, deleteOne, deleteMany } from "@Tools/Db"; -import { GenUUID, SimpleObject, IsNotNullOrEmpty, IsNullOrEmpty } from '@Tools/Misc'; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; -export let requestCollection = 'requests'; +import { GenUUID, SimpleObject, IsNotNullOrEmpty, IsNullOrEmpty } from "@Tools/Misc"; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; + +export const requestCollection = "requests"; export class RequestType { - public static HANDSHAKE = 'handshake'; // doing handshake to make a connection - public static CONNECTION = 'connection'; // asking to make a connection - public static FRIEND = 'friend'; // asking to be a friend - public static FOLLOW = 'follow'; // asking to follow - public static VERIFYEMAIL = 'verifyEmail'; // verifying email request -}; + public static HANDSHAKE = "handshake"; // doing handshake to make a connection + public static CONNECTION = "connection"; // asking to make a connection + public static FRIEND = "friend"; // asking to be a friend + public static FOLLOW = "follow"; // asking to follow + public static VERIFYEMAIL = "verifyEmail"; // verifying email request +} // Initialize request management. // Mostly starts a periodic function that deletes expired requests. export function initRequests(): void { // Expire requests that have pased their prime - setInterval( async () => { + setInterval(async () => { const nowtime = new Date(); const numDeleted = await deleteMany(requestCollection, - new GenericFilter({ 'expirationTime': { $lt: nowtime } }) ); + new GenericFilter({ "expirationTime": { $lt: nowtime } })); if (numDeleted > 0) { Logger.debug(`Requests.Expiration: expired ${numDeleted} requests`); - }; - }, 1000 * 60 * 2 ); -}; + } + }, 1000 * 60 * 2); +} export const Requests = { async getWithId(pRequestId: string): Promise { - return IsNullOrEmpty(pRequestId) ? null : getObject(requestCollection, - new GenericFilter({ 'id': pRequestId })); + return IsNullOrEmpty(pRequestId) + ? null + : getObject(requestCollection, + new GenericFilter({ "id": pRequestId })); }, // Return all Requests that have the passed Id as either the requester or target. // Normally matches the accountId fields but can change it to others (usually for connection NodeId). async getWithRequesterOrTarget(pRequestId: string, pType: string, - pRequesterField: string = 'requestingAccountId', - pTargetField: string = 'targetAccountId'): Promise { - return IsNullOrEmpty(pRequestId) ? null : getObject(requestCollection, + pRequesterField = "requestingAccountId", + pTargetField = "targetAccountId"): Promise { + return IsNullOrEmpty(pRequestId) + ? null + : getObject(requestCollection, new GenericFilter( - { '$or': [ SimpleObject(pRequesterField, pRequestId), - SimpleObject(pTargetField, pRequestId) - ], - 'requestType': pType - }) ); + { "$or": [ + SimpleObject(pRequesterField, pRequestId), + SimpleObject(pTargetField, pRequestId) + ], + "requestType": pType })); }, // Return a Request between the two specified id's // Normally matches the accountId fields but can change it to others (usually for connection NodeId). async getWithRequestBetween(pRequestId: string, pTargetId: string, pType: string, - pRequesterField: string = 'requestingAccountId', - pTargetField: string = 'targetAccountId'): Promise { + pRequesterField = "requestingAccountId", + pTargetField = "targetAccountId"): Promise { const wayone: VKeyedCollection = {}; wayone[pRequesterField] = pRequestId; wayone[pTargetField] = pTargetId; const waytwo: VKeyedCollection = {}; waytwo[pRequesterField] = pTargetId; waytwo[pTargetField] = pRequestId; - return IsNullOrEmpty(pRequestId) ? null : getObject(requestCollection, + return IsNullOrEmpty(pRequestId) + ? null + : getObject(requestCollection, new GenericFilter( - { '$or': [ wayone, waytwo ], - 'requestType': pType - }) ); + { "$or": [wayone, waytwo], + "requestType": pType })); }, create(): RequestEntity { const aRequest = new RequestEntity(); @@ -114,40 +119,37 @@ export const Requests = { newRequest.requestType = RequestType.VERIFYEMAIL; newRequest.requestingAccountId = pAccountId; newRequest.verificationCode = pVerificationCode; - const expirationMinutes = Config["metaverse-server"]['email-verification-timeout-minutes']; + const expirationMinutes = Config["metaverse-server"]["email-verification-timeout-minutes"]; newRequest.expirationTime = new Date(Date.now() + 1000 * 60 * expirationMinutes); return newRequest; }, - add(pRequestEntity: RequestEntity) : Promise { + add(pRequestEntity: RequestEntity): Promise { return createObject(requestCollection, pRequestEntity); }, async update(pEntity: RequestEntity, pFields: VKeyedCollection): Promise { return updateObjectFields(requestCollection, - new GenericFilter({ 'id': pEntity.id }), pFields); + new GenericFilter({ "id": pEntity.id }), pFields); }, - async remove(pRequestEntity: RequestEntity) : Promise { - return deleteOne(requestCollection, new GenericFilter({ 'id': pRequestEntity.id }) ); + async remove(pRequestEntity: RequestEntity): Promise { + return deleteOne(requestCollection, new GenericFilter({ "id": pRequestEntity.id })); }, // Remove all requests for specified account of the specified type // If type not specified, remove them all - async removeAllMyRequests(pAccountId: string, pRequestType?: string): Promise{ + async removeAllMyRequests(pAccountId: string, pRequestType?: string): Promise { const criteria: VKeyedCollection = { - 'requestingAccount': pAccountId + "requestingAccount": pAccountId }; if (IsNotNullOrEmpty(pRequestType)) { criteria.requestType = pRequestType; - }; + } return deleteMany(requestCollection, new GenericFilter(criteria)); }, // TODO: add scope (admin) and filter criteria filtering // It's push down to this routine so we could possibly use DB magic for the queries async *enumerateAsync(pPager: CriteriaFilter, - pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { + pInfoer?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { for await (const acct of getObjects(requestCollection, pPager, pInfoer, pScoper)) { yield acct; - }; + } } }; - - - diff --git a/src/Entities/SessionEntity.ts b/src/Entities/SessionEntity.ts index bb6af1b0..cb568e8d 100755 --- a/src/Entities/SessionEntity.ts +++ b/src/Entities/SessionEntity.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' // Class to manage the manipulations on entities scope of application // Note: *Entity classes cannot have instance functions because they are created from the database @@ -23,4 +22,4 @@ export class SessionEntity { public whenSessionCreated: Date; public timeOfLastReference: Date; public countReference: number; -}; +} diff --git a/src/Entities/Sessions.ts b/src/Entities/Sessions.ts index ec8db511..631f0095 100755 --- a/src/Entities/Sessions.ts +++ b/src/Entities/Sessions.ts @@ -11,17 +11,17 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { SessionEntity } from '@Entities/SessionEntity'; +import { Config } from "@Base/config"; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; +import { SessionEntity } from "@Entities/SessionEntity"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { GenUUID, genRandomString, IsNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; + +import { VKeyedCollection } from "@Tools/vTypes"; +import { GenUUID, genRandomString, IsNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; let _currentSessions: Map; @@ -33,22 +33,22 @@ export function initSessions(): void { _currentSessions = new Map(); // Expire tokens that have pased their prime - setInterval( async () => { + setInterval(async () => { const timeoutTime = new Date(Date.now() - 1000 * 60 * Config["metaverse-server"]["session-timeout-minutes"]).valueOf(); const toDelete: SessionEntity[] = []; - _currentSessions.forEach( sess => { + _currentSessions.forEach((sess) => { if (sess.timeOfLastReference.valueOf() < timeoutTime) { toDelete.push(sess); } }); if (toDelete.length > 0) { Logger.debug(`Session.Expiration: expired ${toDelete.length} sessions`); - toDelete.forEach( sess => { - _currentSessions.delete(sess.senderKey); + toDelete.forEach((sess) => { + _currentSessions["delete"](sess.senderKey); }); - }; - }, 1000 * 60 * 2 ); -}; + } + }, 1000 * 60 * 2); +} export const Sessions = { // Create a new AuthToken. @@ -61,7 +61,7 @@ export const Sessions = { return aSession; }, getSessionWithSessionId(pSessionId: string): SessionEntity { - _currentSessions.forEach( sess => { + _currentSessions.forEach((sess) => { if (pSessionId === sess.id) { return sess; } @@ -71,12 +71,12 @@ export const Sessions = { getSessionWithSenderKey(pSenderKey: string): SessionEntity { return _currentSessions.get(pSenderKey); }, - addSession(pSessionEntity: SessionEntity) : SessionEntity { + addSession(pSessionEntity: SessionEntity): SessionEntity { _currentSessions.set(pSessionEntity.senderKey, pSessionEntity); return pSessionEntity; }, - removeSession(pSessionEntity: SessionEntity) : void { - _currentSessions.delete(pSessionEntity.senderKey); + removeSession(pSessionEntity: SessionEntity): void { + _currentSessions["delete"](pSessionEntity.senderKey); }, // touch a session to not that it has been used touchSession(pSession: SessionEntity): void { @@ -92,13 +92,10 @@ export const Sessions = { if (pFilter) { if (pFilter.criteriaTest(sess)) { yield sess; - }; - } - else { + } + } else { yield sess; - }; - }; - }, + } + } + } }; - - diff --git a/src/Entities/Sets/Availability.ts b/src/Entities/Sets/Availability.ts index b0435715..ad57a752 100644 --- a/src/Entities/Sets/Availability.ts +++ b/src/Entities/Sets/Availability.ts @@ -12,24 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; export class Availability { - public static NONE: string = 'none'; // no one can see me - public static FRIENDS: string = 'friends'; // available to friends - public static CONNECTIONS: string = 'connections'; // available to connections - public static ALL: string = 'all'; // available to all + public static NONE = "none"; // no one can see me + public static FRIENDS = "friends"; // available to friends + public static CONNECTIONS = "connections"; // available to connections + public static ALL = "all"; // available to all - // See if the passed availability code is a known availability token - static async KnownAvailability(pAvailability: string): Promise { - return [ Availability.NONE, + // See if the passed availability code is a known availability token + static async KnownAvailability(pAvailability: string): Promise { + return [ + Availability.NONE, Availability.FRIENDS, Availability.CONNECTIONS, Availability.ALL - ].includes(pAvailability); - }; - -}; + ].includes(pAvailability); + } +} diff --git a/src/Entities/Sets/Maturity.ts b/src/Entities/Sets/Maturity.ts index ffdfd121..4bd74435 100644 --- a/src/Entities/Sets/Maturity.ts +++ b/src/Entities/Sets/Maturity.ts @@ -12,29 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; +import Config from "@Base/config"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; export class Maturity { - public static UNRATED: string = 'unrated'; - public static EVERYONE: string = 'everyone'; - public static TEEN: string = 'teen'; - public static MATURE: string = 'mature'; - public static ADULT: string = 'adult'; - - static MaturityCategories = [ Maturity.UNRATED, - Maturity.EVERYONE, - Maturity.TEEN, - Maturity.MATURE, - Maturity.ADULT - ]; - - static KnownMaturity(pMaturity: string): boolean { - return this.MaturityCategories.includes(pMaturity); - }; - -}; - + public static UNRATED = "unrated"; + public static EVERYONE = "everyone"; + public static TEEN = "teen"; + public static MATURE = "mature"; + public static ADULT = "adult"; + + static MaturityCategories = [ + Maturity.UNRATED, + Maturity.EVERYONE, + Maturity.TEEN, + Maturity.MATURE, + Maturity.ADULT + ]; + + static KnownMaturity(pMaturity: string): boolean { + return this.MaturityCategories.includes(pMaturity); + } + +} diff --git a/src/Entities/Sets/Restriction.ts b/src/Entities/Sets/Restriction.ts index eef6e548..1442b368 100644 --- a/src/Entities/Sets/Restriction.ts +++ b/src/Entities/Sets/Restriction.ts @@ -12,25 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; +import Config from "@Base/config"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; export class Restriction { - public static OPEN: string = 'open'; - public static HIFI: string = 'hifi'; - public static ACL: string = 'acl'; + public static OPEN = "open"; + public static HIFI = "hifi"; + public static ACL = "acl"; - static RestrictionCategories = [ - Restriction.OPEN, - Restriction.HIFI, - Restriction.ACL - ]; + static RestrictionCategories = [ + Restriction.OPEN, + Restriction.HIFI, + Restriction.ACL + ]; - static KnownRestriction(pRestriction: string): boolean { - return this.RestrictionCategories.includes(pRestriction); - }; + static KnownRestriction(pRestriction: string): boolean { + return this.RestrictionCategories.includes(pRestriction); + } -}; +} diff --git a/src/Entities/Sets/Roles.ts b/src/Entities/Sets/Roles.ts index 4836e873..39baa012 100644 --- a/src/Entities/Sets/Roles.ts +++ b/src/Entities/Sets/Roles.ts @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' import { IsNotNullOrEmpty } from "@Tools/Misc"; // Class to manage the manipulations on roles that accounts can have export class Roles { - // at the moment, the only role is 'admin' - public static ADMIN: string = 'admin'; // someone who has metaverse-server admin - public static USER: string = 'user'; // a 'user' or 'person' + // at the moment, the only role is 'admin' + public static ADMIN = "admin"; // someone who has metaverse-server admin + public static USER = "user"; // a 'user' or 'person' - // See if the passed role code is a known role token - static async KnownRole(pScope: string): Promise { - return [ Roles.ADMIN, Roles.USER ].includes(pScope); - }; + // See if the passed role code is a known role token + static async KnownRole(pScope: string): Promise { + return [Roles.ADMIN, Roles.USER].includes(pScope); + } -}; +} diff --git a/src/Entities/Sets/Visibility.ts b/src/Entities/Sets/Visibility.ts index 4b15a6f4..8466f332 100644 --- a/src/Entities/Sets/Visibility.ts +++ b/src/Entities/Sets/Visibility.ts @@ -12,25 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' export class Visibility { - public static OPEN: string = 'open'; - public static FRIENDS: string = 'friends'; - public static CONNECTIONS: string = 'connections'; - public static GROUP: string = 'group'; - public static PRIVATE: string = 'private'; + public static OPEN = "open"; + public static FRIENDS = "friends"; + public static CONNECTIONS = "connections"; + public static GROUP = "group"; + public static PRIVATE = "private"; static VisibilityCategories = [ - Visibility.OPEN, - Visibility.FRIENDS, - Visibility.CONNECTIONS, - Visibility.GROUP, - Visibility.PRIVATE + Visibility.OPEN, + Visibility.FRIENDS, + Visibility.CONNECTIONS, + Visibility.GROUP, + Visibility.PRIVATE ]; static KnownVisibility(pVisibility: string): boolean { return this.VisibilityCategories.includes(pVisibility); - }; -}; - + } +} diff --git a/src/Entities/Tokens.ts b/src/Entities/Tokens.ts index 451233ba..d478360c 100755 --- a/src/Entities/Tokens.ts +++ b/src/Entities/Tokens.ts @@ -11,54 +11,54 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { AuthToken } from '@Entities/AuthToken'; -import { createObject, getObject, getObjects, updateObjectFields, deleteMany, deleteOne } from '@Tools/Db'; +import { Config } from "@Base/config"; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; +import { AuthToken } from "@Entities/AuthToken"; +import { createObject, getObject, getObjects, updateObjectFields, deleteMany, deleteOne } from "@Tools/Db"; -import { SArray, VKeyedCollection } from '@Tools/vTypes'; -import { Clamp, GenUUID, IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; -export let tokenCollection = 'tokens'; +import { SArray, VKeyedCollection } from "@Tools/vTypes"; +import { Clamp, GenUUID, IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; + +export const tokenCollection = "tokens"; // Initialize token management. // Mostly starts a periodic function that deletes expired tokens. export function initTokens(): void { // Expire tokens that have pased their prime - setInterval( async () => { + setInterval(async () => { const nowtime = new Date(); const deletedTokens = await deleteMany(tokenCollection, - new GenericFilter({ 'expirationTime': { $lt: nowtime } }) ); + new GenericFilter({ "expirationTime": { $lt: nowtime } })); if (deletedTokens > 0) { Logger.debug(`Tokens.Expiration: expired ${deletedTokens} tokens`); - }; - }, 1000 * 60 * 5 ); -}; + } + }, 1000 * 60 * 5); +} // Class to manage the manipulations on entities scope of application export class TokenScope { - public static OWNER: string = 'owner'; // a 'user' or 'person' - public static DOMAIN: string = 'domain'; // a domain-server - public static PLACE: string = 'place'; // a Place (APIkey) + public static OWNER = "owner"; // a 'user' or 'person' + public static DOMAIN = "domain"; // a domain-server + public static PLACE = "place"; // a Place (APIkey) // Added for ActivityPub access control - public static READ: string = 'read'; - public static WRITE: string = 'write'; + public static READ = "read"; + public static WRITE = "write"; // See if the passed scope token is a known scope code. static KnownScope(pScope: string): boolean { - return [ TokenScope.OWNER, TokenScope.DOMAIN, TokenScope.PLACE ].includes(pScope); - }; -}; + return [TokenScope.OWNER, TokenScope.DOMAIN, TokenScope.PLACE].includes(pScope); + } +} export const Tokens = { // Create a new AuthToken. - async createToken(pAccountId: string, pScope: string[], pExpireHours: number = 0): Promise { + async createToken(pAccountId: string, pScope: string[], pExpireHours = 0): Promise { const aToken = new AuthToken(); aToken.id = GenUUID(); aToken.token = GenUUID(); @@ -66,8 +66,10 @@ export const Tokens = { aToken.accountId = pAccountId; // Verify that passed scopes are known scope codes aToken.scope = []; - pScope.forEach( aScope => { - if (TokenScope.KnownScope(aScope)) aToken.scope.push(aScope); + pScope.forEach((aScope) => { + if (TokenScope.KnownScope(aScope)) { + aToken.scope.push(aScope); + } }); aToken.whenCreated = new Date(); @@ -83,51 +85,58 @@ export const Tokens = { default: // There is a specification of some hours to expire const hours = Clamp(pExpireHours, 1, 1000000); // max is 114 years - aToken.expirationTime = new Date(new Date().valueOf() + hours * 1000*60*60); + aToken.expirationTime = new Date(new Date().valueOf() + hours * 1000 * 60 * 60); break; - }; + } return aToken; }, async getTokenWithTokenId(pTokenId: string): Promise { - return IsNullOrEmpty(pTokenId) ? null : getObject(tokenCollection, - new GenericFilter({ 'id': pTokenId })); + return IsNullOrEmpty(pTokenId) + ? null + : getObject(tokenCollection, + new GenericFilter({ "id": pTokenId })); }, async getTokenWithToken(pToken: string): Promise { - return IsNullOrEmpty(pToken) ? null : getObject(tokenCollection, - new GenericFilter({ 'token': pToken })); + return IsNullOrEmpty(pToken) + ? null + : getObject(tokenCollection, + new GenericFilter({ "token": pToken })); }, async getTokenWithRefreshToken(pToken: string): Promise { - return IsNullOrEmpty(pToken) ? null : getObject(tokenCollection, - new GenericFilter({ 'refreshToken': pToken })); + return IsNullOrEmpty(pToken) + ? null + : getObject(tokenCollection, + new GenericFilter({ "refreshToken": pToken })); }, - async addToken(pAuthToken: AuthToken) : Promise { + async addToken(pAuthToken: AuthToken): Promise { return createObject(tokenCollection, pAuthToken); }, - async removeToken(pAuthToken: AuthToken) : Promise { - return deleteOne(tokenCollection, new GenericFilter({ 'id': pAuthToken.id })); }, + async removeToken(pAuthToken: AuthToken): Promise { + return deleteOne(tokenCollection, new GenericFilter({ "id": pAuthToken.id })); + }, async updateTokenFields(pEntity: AuthToken, pFields: VKeyedCollection): Promise { return updateObjectFields(tokenCollection, - new GenericFilter({ 'id': pEntity.id }), pFields); + new GenericFilter({ "id": pEntity.id }), pFields); }, // Return all the tokens that were generated by a specific accountId async *getTokensForOwner(pOwnerId: string, pPager?: CriteriaFilter): AsyncGenerator { return IsNullOrEmpty(pOwnerId) - ? null - : Tokens.enumerateAsync(new GenericFilter({ 'accountId': pOwnerId }), pPager); + ? null + : Tokens.enumerateAsync(new GenericFilter({ "accountId": pOwnerId }), pPager); }, async *enumerateAsync(pFilter?: CriteriaFilter, pPager?: CriteriaFilter, pScoper?: CriteriaFilter): AsyncGenerator { for await (const tok of getObjects(tokenCollection, pFilter, pPager, pScoper)) { - yield tok; - }; + yield tok; + } // return getObjects(tokenCollection, pFilter, pPager, pScoper); // not sure why this does't work }, // Return an expiration date for the token depending on its scope computeDefaultExpiration(pScopes: string[], pBaseDate?: Date): Date { return new Date((pBaseDate ? pBaseDate.valueOf() : new Date().valueOf()) - + ( SArray.has(pScopes, TokenScope.DOMAIN) - ? Config.auth["domain-token-expire-hours"] * 1000*60*60 - : Config.auth["owner-token-expire-hours"] * 1000*60*60 - ) + + (SArray.has(pScopes, TokenScope.DOMAIN) + ? Config.auth["domain-token-expire-hours"] * 1000 * 60 * 60 + : Config.auth["owner-token-expire-hours"] * 1000 * 60 * 60 + ) ); }, // Return 'true' if the passed token has not expired. @@ -145,9 +154,9 @@ export const Tokens = { aToken.refreshToken = GenUUID(); aToken.accountId = GenUUID(); // account that doesn't exist // Verify that passed scopes are known scope codes - aToken.scope = [ TokenScope.OWNER ]; + aToken.scope = [TokenScope.OWNER]; aToken.whenCreated = new Date(); - aToken.expirationTime = new Date( new Date().valueOf() + 1000 ); // only lasts one second + aToken.expirationTime = new Date(new Date().valueOf() + 1000); // only lasts one second return aToken; }, isSpecialAdminToken(pAuthToken: AuthToken): boolean { diff --git a/src/Monitoring/CounterStat.ts b/src/Monitoring/CounterStat.ts index 54ecfb08..69ddfc53 100755 --- a/src/Monitoring/CounterStat.ts +++ b/src/Monitoring/CounterStat.ts @@ -11,32 +11,33 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { Stat, updateValueFunction } from '@Monitoring/Stat'; +import { Config } from "@Base/config"; + +import { Stat, updateValueFunction } from "@Monitoring/Stat"; export class CounterStat extends Stat { constructor(pName: string, pCatagory: string, pUnit: string, pPullAction?: updateValueFunction) { super(pName, pCatagory, pUnit, pPullAction); - }; + } // Record an event has happened - Event(pCount: number = 1): void { + Event(pCount = 1): void { this.value += pCount; - this._histograms.forEach( (histo) => { + this._histograms.forEach((histo) => { histo.Event(pCount); }); - }; + } + async Gather(): Promise { await this.DoPullAction(); - return; - }; - Report(pReturnHistogram: boolean = true): any { + } + + Report(pReturnHistogram = true): any { const report = super.Report(pReturnHistogram); return report; - }; -} \ No newline at end of file + } +} diff --git a/src/Monitoring/EventHistogram.ts b/src/Monitoring/EventHistogram.ts index 14d09866..5ee4ea27 100755 --- a/src/Monitoring/EventHistogram.ts +++ b/src/Monitoring/EventHistogram.ts @@ -11,13 +11,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { Histogram } from '@Monitoring/Histogram'; +import { Config } from "@Base/config"; -import { Logger } from '@Tools/Logging'; +import { Histogram } from "@Monitoring/Histogram"; + +import { Logger } from "@Tools/Logging"; export class EventHistogram extends Histogram { @@ -35,12 +35,12 @@ export class EventHistogram extends Histogram { this._totalHistogramMilliseconds = this._numBuckets * this._bucketMilliseconds; this.Zero(); - this._lastBucket = 0 + this._lastBucket = 0; this._timeBase = Date.now().valueOf(); - }; + } // Add some events to the histogram. - Event(pNumberOfEvents: number = 1) { + Event(pNumberOfEvents = 1) { const bucketTime = Date.now().valueOf() - this._timeBase; // from the base of the array, where could this item go @@ -52,42 +52,41 @@ export class EventHistogram extends Histogram { this._lastBucket = 0; bucket -= this._numBuckets; this._timeBase += this._totalHistogramMilliseconds; - } - else { + } else { this._lastBucket++; } this._histogram[this._lastBucket] = 0; - }; + } this._histogram[this._lastBucket] += pNumberOfEvents; - }; + } // Returns an object with all the information about the histogram GetHistogram(): any { - const values: number[] = [] + const values: number[] = []; values.length = this._numBuckets; let idx = this._lastBucket; - for (let ii=0; ii < this._numBuckets; ii++) { + for (let ii = 0; ii < this._numBuckets; ii++) { if (++idx >= this._numBuckets) { idx = 0; - }; + } values[ii] = this._histogram[idx]; - }; + } return { "buckets": this._numBuckets, "bucketMilliseconds": this._bucketMilliseconds, "totalMilliseconds": this._totalHistogramMilliseconds, - "timeBase": this._timeBase - (this._bucketMilliseconds * (this._numBuckets - this._lastBucket)), - "baseNumber": Math.floor((this._timeBase / this._bucketMilliseconds)) + this._lastBucket + 1, + "timeBase": this._timeBase - this._bucketMilliseconds * (this._numBuckets - this._lastBucket), + "baseNumber": Math.floor(this._timeBase / this._bucketMilliseconds) + this._lastBucket + 1, "type": "accumulation", - "values": values + values }; - }; + } // Zero out the current histogram Zero(): void { this._histogram = []; this._histogram.length = this._numBuckets; this._histogram.fill(0); - }; -}; \ No newline at end of file + } +} diff --git a/src/Monitoring/Histogram.ts b/src/Monitoring/Histogram.ts index bb851942..6de62f82 100755 --- a/src/Monitoring/Histogram.ts +++ b/src/Monitoring/Histogram.ts @@ -11,10 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' + export abstract class Histogram { abstract Event(pCount: number): void; abstract GetHistogram(): any; } - diff --git a/src/Monitoring/Monitoring.ts b/src/Monitoring/Monitoring.ts index f899e09c..c28742dc 100755 --- a/src/Monitoring/Monitoring.ts +++ b/src/Monitoring/Monitoring.ts @@ -11,16 +11,16 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { Stat } from '@Monitoring/Stat'; -import { StatsOs } from '@Monitoring/StatsOs'; -import { StatsMetaverse } from '@Monitoring/StatsMetaverse'; -import { StatsServer } from '@Monitoring/StatsServer'; +import { Config } from "@Base/config"; -import { Logger } from '@Tools/Logging'; +import { Stat } from "@Monitoring/Stat"; +import { StatsOs } from "@Monitoring/StatsOs"; +import { StatsMetaverse } from "@Monitoring/StatsMetaverse"; +import { StatsServer } from "@Monitoring/StatsServer"; + +import { Logger } from "@Tools/Logging"; let _statistics: Map; @@ -32,13 +32,13 @@ export function initMonitoring(): void { CreateStats(); // Stats are called once a second so they can gather histograms - setInterval( async () => { + setInterval(async () => { for (const stat of _statistics.values()) { await stat.Gather(); - }; - }, 1000 * 1 ); - }; -}; + } + }, 1000 * 1); + } +} export const Monitoring = { addStat(pStat: Stat) { @@ -53,11 +53,11 @@ export const Monitoring = { // Add event count to a named Stat. // This allows stats to be optional because the caller doesn't need to // know if the stat exists. - event(pEventName: string, pCount: number = 1): void { + event(pEventName: string, pCount = 1): void { const theStat = _statistics?.get(pEventName); if (theStat) { theStat.Event(pCount); - }; + } } }; @@ -65,4 +65,4 @@ function CreateStats() { Monitoring.addStat(new StatsOs()); Monitoring.addStat(new StatsMetaverse()); Monitoring.addStat(new StatsServer()); -}; +} diff --git a/src/Monitoring/Stat.ts b/src/Monitoring/Stat.ts index 3a418e14..c0942de8 100755 --- a/src/Monitoring/Stat.ts +++ b/src/Monitoring/Stat.ts @@ -11,16 +11,16 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { Histogram } from '@Monitoring/Histogram'; -import { Logger } from '@Tools/Logging'; -import { VKeyedCollection } from '@Tools/vTypes'; +import { Config } from "@Base/config"; + +import { Histogram } from "@Monitoring/Histogram"; +import { Logger } from "@Tools/Logging"; +import { VKeyedCollection } from "@Tools/vTypes"; // Function that is called for a statistic when pulling its value -export type updateValueFunction = ( pStat: Stat ) => Promise; +export type updateValueFunction = (pStat: Stat) => Promise; export abstract class Stat { public name: string; // the name of the statistic @@ -35,34 +35,34 @@ export abstract class Stat { public secondsBetweenPulls: number; // seconds between doing pull operation public pullCount: number; - _histograms: Map; + _histograms: Map; - constructor(pName:string, pCategory: string, pUnit: string, pPullAction?: updateValueFunction) { + constructor(pName: string, pCategory: string, pUnit: string, pPullAction?: updateValueFunction) { this.name = pName; this.category = pCategory; this.unit = pUnit; this.value = 0; this.pullAction = pPullAction; - this._histograms = new Map(); - }; + this._histograms = new Map(); + } // Add a histogram to this value AddHistogram(pHistogramName: string, pHistogram: Histogram) { this._histograms.set(pHistogramName, pHistogram); - }; + } + async DoPullAction(): Promise { if (this.pullAction) { if (this.secondsBetweenPulls) { if (this.pullCount-- < 0) { await this.pullAction(this); this.pullCount = this.secondsBetweenPulls; - }; - } - else { + } + } else { await this.pullAction(this); - }; - }; - }; + } + } + } // Record one or more events abstract Event(pCount: number): void; @@ -71,21 +71,20 @@ export abstract class Stat { abstract Gather(): void; // Return an object containing the values in this stat - Report(pReturnHistogram: boolean = true): any { + Report(pReturnHistogram = true): any { const report: VKeyedCollection = { - 'name': this.name, - 'category': this.category, - 'unit': this.unit, - 'value': this.value + "name": this.name, + "category": this.category, + "unit": this.unit, + "value": this.value }; if (pReturnHistogram && this._histograms.size > 0) { const history: any = {}; - this._histograms.forEach( (histo, name) => { + this._histograms.forEach((histo, name) => { history[name] = histo.GetHistogram(); }); report.history = history; - }; + } return report; - }; -}; - + } +} diff --git a/src/Monitoring/StatsMetaverse.ts b/src/Monitoring/StatsMetaverse.ts index 15cd23d6..4b1dce1d 100755 --- a/src/Monitoring/StatsMetaverse.ts +++ b/src/Monitoring/StatsMetaverse.ts @@ -11,96 +11,96 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { Accounts } from '@Entities/Accounts'; +import { Config } from "@Base/config"; -import { Monitoring } from '@Monitoring/Monitoring'; -import { Stat } from '@Monitoring/Stat'; -import { ValueStat } from '@Monitoring/ValueStat'; -import { ValueHistogram } from '@Monitoring/ValueHistogram'; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; -import { Domains } from '@Entities/Domains'; +import { Accounts } from "@Entities/Accounts"; -import { Logger } from '@Tools/Logging'; +import { Monitoring } from "@Monitoring/Monitoring"; +import { Stat } from "@Monitoring/Stat"; +import { ValueStat } from "@Monitoring/ValueStat"; +import { ValueHistogram } from "@Monitoring/ValueHistogram"; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; +import { Domains } from "@Entities/Domains"; + +import { Logger } from "@Tools/Logging"; // All the OS statistics export class StatsMetaverse extends Stat { constructor() { - super('metaverse', 'metaverse', ''); + super("metaverse", "metaverse", ""); // Count of all accounts that are sending heartbeats - const totalOnline = new ValueStat('totalOnline', 'metaverse', 'count', async (stat) => { + const totalOnline = new ValueStat("totalOnline", "metaverse", "count", async (stat) => { const numOnline = await Accounts.accountCount( - new GenericFilter( { 'timeOfLastHeartbeat': { '$gte': Accounts.dateWhenNotOnline() }}) + new GenericFilter({ "timeOfLastHeartbeat": { "$gte": Accounts.dateWhenNotOnline() } }) ); stat.Event(numOnline); }); if (Config.monitoring.history) { - totalOnline.AddHistogram('perDay', new ValueHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - totalOnline.AddHistogram('perWeek', new ValueHistogram(24*7, 60*60*1000));// one week per hour - }; + totalOnline.AddHistogram("perDay", new ValueHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + totalOnline.AddHistogram("perWeek", new ValueHistogram(24 * 7, 60 * 60 * 1000));// one week per hour + } Monitoring.addStat(totalOnline); // Sum of all users reported by domains. // NOTE: this also sets the value for the anon user stat to save the extra DB query - const domainTotalUsers = new ValueStat('domainTotalUsers', 'metaverse', 'count', async (stat) => { + const domainTotalUsers = new ValueStat("domainTotalUsers", "metaverse", "count", async (stat) => { let totalUsers = 0; let anonUsers = 0; for await (const aDomain of Domains.enumerateAsync(new GenericFilter( - { '$or': [ { 'numUsers': { '$gt': 0 } }, { 'anonUsers': { '$gt': 0 } } ] - }) ) ) { + { "$or": [{ "numUsers": { "$gt": 0 } }, { "anonUsers": { "$gt": 0 } }] }))) { totalUsers += aDomain.numUsers; anonUsers += aDomain.anonUsers; - }; + } stat.Event(totalUsers); - Monitoring.getStat('domainAnonUsers')?.Event(anonUsers); + Monitoring.getStat("domainAnonUsers")?.Event(anonUsers); }); if (Config.monitoring.history) { - domainTotalUsers.AddHistogram('perDay', new ValueHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - domainTotalUsers.AddHistogram('perWeek', new ValueHistogram(24*7, 60*60*1000));// one week per hour - }; + domainTotalUsers.AddHistogram("perDay", new ValueHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + domainTotalUsers.AddHistogram("perWeek", new ValueHistogram(24 * 7, 60 * 60 * 1000));// one week per hour + } Monitoring.addStat(domainTotalUsers); // Sum of all anon users reported by domains. // NOTE: value is set by domainTotalUsers - const domainAnonUsers = new ValueStat('domainAnonUsers', 'metaverse', 'count'); + const domainAnonUsers = new ValueStat("domainAnonUsers", "metaverse", "count"); if (Config.monitoring.history) { - domainAnonUsers.AddHistogram('perDay', new ValueHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - domainAnonUsers.AddHistogram('perWeek', new ValueHistogram(24*7, 60*60*1000));// one week per hour - }; + domainAnonUsers.AddHistogram("perDay", new ValueHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + domainAnonUsers.AddHistogram("perWeek", new ValueHistogram(24 * 7, 60 * 60 * 1000));// one week per hour + } Monitoring.addStat(domainAnonUsers); // Number of domains that are sending heartbeats - const activeDomains = new ValueStat('activeDomains', 'metaverse', 'count', async (stat) => { + const activeDomains = new ValueStat("activeDomains", "metaverse", "count", async (stat) => { const numDomains = await Domains.domainCount( - new GenericFilter( { 'timeOfLastHeartbeat': { '$gte': Domains.dateWhenNotActive() }}) + new GenericFilter({ "timeOfLastHeartbeat": { "$gte": Domains.dateWhenNotActive() } }) ); stat.Event(numDomains); }); if (Config.monitoring.history) { - activeDomains.AddHistogram('perDay', new ValueHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - activeDomains.AddHistogram('perWeek', new ValueHistogram(24*7, 60*60*1000));// one week per hour - }; + activeDomains.AddHistogram("perDay", new ValueHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + activeDomains.AddHistogram("perWeek", new ValueHistogram(24 * 7, 60 * 60 * 1000));// one week per hour + } Monitoring.addStat(activeDomains); - }; + } Event(pCount: number): void { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } + async Gather(): Promise { - return; + } - Report(pReturnHistogram: boolean = true): any { + + Report(pReturnHistogram = true): any { return { - 'totalOnline': Monitoring.getStat('totalOnline')?.Report(pReturnHistogram), - 'domainTotalUsers': Monitoring.getStat('domainTotalUsers')?.Report(pReturnHistogram), - 'domainAnonUsers': Monitoring.getStat('domainAnonUsers')?.Report(pReturnHistogram), - 'activeDomains': Monitoring.getStat('activeDomains')?.Report(pReturnHistogram), + "totalOnline": Monitoring.getStat("totalOnline")?.Report(pReturnHistogram), + "domainTotalUsers": Monitoring.getStat("domainTotalUsers")?.Report(pReturnHistogram), + "domainAnonUsers": Monitoring.getStat("domainAnonUsers")?.Report(pReturnHistogram), + "activeDomains": Monitoring.getStat("activeDomains")?.Report(pReturnHistogram) }; } -}; - +} diff --git a/src/Monitoring/StatsOs.ts b/src/Monitoring/StatsOs.ts index 963d3ee3..4ecb93b6 100755 --- a/src/Monitoring/StatsOs.ts +++ b/src/Monitoring/StatsOs.ts @@ -11,68 +11,70 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import os from 'os'; +import { Config } from "@Base/config"; -import { Monitoring } from '@Monitoring/Monitoring'; -import { Stat } from '@Monitoring/Stat'; -import { ValueStat } from '@Monitoring/ValueStat'; -import { ValueHistogram } from '@Monitoring/ValueHistogram'; -import { Logger } from '@Tools/Logging'; +import os from "os"; + +import { Monitoring } from "@Monitoring/Monitoring"; +import { Stat } from "@Monitoring/Stat"; +import { ValueStat } from "@Monitoring/ValueStat"; +import { ValueHistogram } from "@Monitoring/ValueHistogram"; +import { Logger } from "@Tools/Logging"; // All the OS statistics export class StatsOs extends Stat { constructor() { - super('os', 'os', ''); + super("os", "os", ""); // Stat that collects the percent of CPU busy - const cpuBusy = new ValueStat('cpuBusy', 'os', 'percent', async (stat) => { + const cpuBusy = new ValueStat("cpuBusy", "os", "percent", async (stat) => { const cpuInfo = os.cpus(); let totalBusy = 0; let totalIdle = 0; - cpuInfo.forEach(element => { + cpuInfo.forEach((element) => { totalIdle += element.times.idle; totalBusy += element.times.user + element.times.nice + element.times.sys + element.times.irq; }); - stat.Event( totalBusy / (totalIdle + totalBusy) * 100); + stat.Event(totalBusy / (totalIdle + totalBusy) * 100); }); if (Config.monitoring.history) { - cpuBusy.AddHistogram('5mins', new ValueHistogram(60, 5 * 1000)); // 60 five second buckets - cpuBusy.AddHistogram('perHour', new ValueHistogram(60, 60*1000)); // 60 one minute buckets - cpuBusy.AddHistogram('perDay', new ValueHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - }; + cpuBusy.AddHistogram("5mins", new ValueHistogram(60, 5 * 1000)); // 60 five second buckets + cpuBusy.AddHistogram("perHour", new ValueHistogram(60, 60 * 1000)); // 60 one minute buckets + cpuBusy.AddHistogram("perDay", new ValueHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + } Monitoring.addStat(cpuBusy); // Stat that collects the percent of memory used - const memUsage = new ValueStat('memUsage', 'os', 'percent', async (stat) => { - stat.Event( os.freemem() / os.totalmem() * 100 ); + const memUsage = new ValueStat("memUsage", "os", "percent", async (stat) => { + stat.Event(os.freemem() / os.totalmem() * 100); }); if (Config.monitoring.history) { - memUsage.AddHistogram('perDay', new ValueHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - memUsage.AddHistogram('perWeek', new ValueHistogram(7*24, 60*60*1000));// one weeks's worth of hour buckets - }; + memUsage.AddHistogram("perDay", new ValueHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + memUsage.AddHistogram("perWeek", new ValueHistogram(7 * 24, 60 * 60 * 1000));// one weeks's worth of hour buckets + } Monitoring.addStat(memUsage); - }; + } Event(pCount: number): void { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } + Gather(): void { - return; + } - Report(pReturnHistogram: boolean = true): any { + + Report(pReturnHistogram = true): any { return { - 'cpus': os.cpus(), - 'freemem': os.freemem(), - 'totalmem': os.totalmem(), - 'loadavg': os.loadavg(), - 'uptime': os.uptime(), - 'cpuBusy': Monitoring.getStat('cpuBusy')?.Report(pReturnHistogram), - 'memUsage': Monitoring.getStat('memUsage')?.Report(pReturnHistogram) + "cpus": os.cpus(), + "freemem": os.freemem(), + "totalmem": os.totalmem(), + "loadavg": os.loadavg(), + "uptime": os.uptime(), + "cpuBusy": Monitoring.getStat("cpuBusy")?.Report(pReturnHistogram), + "memUsage": Monitoring.getStat("memUsage")?.Report(pReturnHistogram) }; } -}; +} diff --git a/src/Monitoring/StatsServer.ts b/src/Monitoring/StatsServer.ts index 23018372..160be899 100755 --- a/src/Monitoring/StatsServer.ts +++ b/src/Monitoring/StatsServer.ts @@ -11,50 +11,52 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import os from 'os'; +import { Config } from "@Base/config"; -import { Monitoring } from '@Monitoring/Monitoring'; -import { Stat } from '@Monitoring/Stat'; -import { CounterStat } from '@Monitoring/CounterStat'; -import { EventHistogram } from '@Monitoring/EventHistogram'; +import os from "os"; + +import { Monitoring } from "@Monitoring/Monitoring"; +import { Stat } from "@Monitoring/Stat"; +import { CounterStat } from "@Monitoring/CounterStat"; +import { EventHistogram } from "@Monitoring/EventHistogram"; // All the OS statistics export class StatsServer extends Stat { constructor() { - super('server', 'server', ''); + super("server", "server", ""); // Number of API requests - const apiStat = new CounterStat('apiRequests', 'server', 'request'); + const apiStat = new CounterStat("apiRequests", "server", "request"); if (Config.monitoring.history) { - apiStat.AddHistogram('perHour', new EventHistogram(60, 60*1000)); // 60 one minute buckets - apiStat.AddHistogram('perDay', new EventHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - apiStat.AddHistogram('perWeek', new EventHistogram(7*24, 60*60*1000));// one weeks worth of one hour buckets - }; + apiStat.AddHistogram("perHour", new EventHistogram(60, 60 * 1000)); // 60 one minute buckets + apiStat.AddHistogram("perDay", new EventHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + apiStat.AddHistogram("perWeek", new EventHistogram(7 * 24, 60 * 60 * 1000));// one weeks worth of one hour buckets + } Monitoring.addStat(apiStat); // Number of requests that generated errors - const apiErrors = new CounterStat('apiErrors', 'server', 'request'); + const apiErrors = new CounterStat("apiErrors", "server", "request"); if (Config.monitoring.history) { - apiStat.AddHistogram('perHour', new EventHistogram(60, 60*1000)); // 60 one minute buckets - apiStat.AddHistogram('perDay', new EventHistogram(24*12, 5*60*1000));// one day's worth of 5 minute buckets - }; + apiStat.AddHistogram("perHour", new EventHistogram(60, 60 * 1000)); // 60 one minute buckets + apiStat.AddHistogram("perDay", new EventHistogram(24 * 12, 5 * 60 * 1000));// one day's worth of 5 minute buckets + } Monitoring.addStat(apiErrors); - }; + } Event(pCount: number): void { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } + Gather(): void { - return; + } - Report(pReturnHistogram: boolean = true) { + + Report(pReturnHistogram = true) { return { - 'apiRequests': Monitoring.getStat('apiRequests')?.Report(pReturnHistogram), - 'apiErrors': Monitoring.getStat('apiErrors')?.Report(pReturnHistogram), + "apiRequests": Monitoring.getStat("apiRequests")?.Report(pReturnHistogram), + "apiErrors": Monitoring.getStat("apiErrors")?.Report(pReturnHistogram) }; } -}; \ No newline at end of file +} diff --git a/src/Monitoring/ValueHistogram.ts b/src/Monitoring/ValueHistogram.ts index 03b4accf..665f9ed2 100755 --- a/src/Monitoring/ValueHistogram.ts +++ b/src/Monitoring/ValueHistogram.ts @@ -11,18 +11,18 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { Histogram } from '@Monitoring/Histogram'; +import { Config } from "@Base/config"; + +import { Histogram } from "@Monitoring/Histogram"; interface BucketValue { - values: number[]; - average?: number; - mean?: number; - mode?: number; -}; + values: number[]; + average?: number; + mean?: number; + mode?: number; +} // Create histogram of averages of values. // This is for like creating a histogram of CPU percent, for instance. @@ -44,9 +44,9 @@ export class ValueHistogram extends Histogram { this._totalHistogramMilliseconds = this._numBuckets * this._bucketMilliseconds; this.Zero(); - this._lastBucket = 0 + this._lastBucket = 0; this._timeBase = Date.now().valueOf(); - }; + } // Add some events to the histogram. Event(pNewValue: number) { @@ -61,56 +61,56 @@ export class ValueHistogram extends Histogram { this._lastBucket = 0; bucket -= this._numBuckets; this._timeBase += this._totalHistogramMilliseconds; - } - else { + } else { this._lastBucket++; } - this._histogram[this._lastBucket] = { 'values': [] }; - }; + this._histogram[this._lastBucket] = { "values": [] }; + } this._histogram[this._lastBucket].values.push(pNewValue); this._histogram[this._lastBucket].average = undefined; - }; + } // Returns an object with all the information about the histogram GetHistogram(): any { const values: number[] = []; let idx = this._lastBucket; - for (let ii=0; ii < this._numBuckets; ii++) { + for (let ii = 0; ii < this._numBuckets; ii++) { if (++idx >= this._numBuckets) { idx = 0; - }; + } const bucketInfo = this._histogram[idx]; let average = this._histogram[idx].average; - if (typeof(average) === 'undefined') { + if (typeof average === "undefined") { if (bucketInfo.values.length > 0) { - const sum = bucketInfo.values.reduce( (a,b) => a + b ); + const sum = bucketInfo.values.reduce((a, b) => { + return a + b; + }); average = sum / bucketInfo.values.length; - } - else { + } else { average = 0; - }; + } this._histogram[idx].average = average; } values.push(average); - }; + } return { "buckets": this._numBuckets, "bucketMilliseconds": this._bucketMilliseconds, "totalMilliseconds": this._totalHistogramMilliseconds, - "timeBase": this._timeBase - (this._bucketMilliseconds * (this._numBuckets - this._lastBucket)), - "baseNumber": Math.floor((this._timeBase / this._bucketMilliseconds)) + this._lastBucket + 1, + "timeBase": this._timeBase - this._bucketMilliseconds * (this._numBuckets - this._lastBucket), + "baseNumber": Math.floor(this._timeBase / this._bucketMilliseconds) + this._lastBucket + 1, "type": "average", - "values": values + values }; - }; + } // Zero out the current histogram Zero(): void { this._histogram = []; this._histogram.length = this._numBuckets; - for (let ii=0; ii < this._numBuckets; ii++) { - this._histogram[ii] = { 'values': [] }; - }; - }; -}; \ No newline at end of file + for (let ii = 0; ii < this._numBuckets; ii++) { + this._histogram[ii] = { "values": [] }; + } + } +} diff --git a/src/Monitoring/ValueStat.ts b/src/Monitoring/ValueStat.ts index e55ad1c8..d222b6ee 100755 --- a/src/Monitoring/ValueStat.ts +++ b/src/Monitoring/ValueStat.ts @@ -11,34 +11,34 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import { Stat, updateValueFunction } from '@Monitoring/Stat'; +import { Config } from "@Base/config"; + +import { Stat, updateValueFunction } from "@Monitoring/Stat"; export class ValueStat extends Stat { constructor(pName: string, pCatagory: string, pUnit: string, pPullAction?: updateValueFunction) { - super(pName, pCatagory, pUnit, pPullAction) - }; + super(pName, pCatagory, pUnit, pPullAction); + } // Remember the current value for this stat Event(pValue: number): void { this.value = pValue; - this._histograms.forEach( (histo) => { + this._histograms.forEach((histo) => { histo.Event(pValue); }); - }; + } // Called once a second. We don't gather numbers every time async Gather(): Promise { await this.DoPullAction(); - return; - }; - Report(pReturnHistogram: boolean = true): any { + } + + Report(pReturnHistogram = true): any { const report = super.Report(pReturnHistogram); return report; - }; -} \ No newline at end of file + } +} diff --git a/src/RequestAdditions.ts b/src/RequestAdditions.ts index 7b62449f..6aa01e40 100755 --- a/src/RequestAdditions.ts +++ b/src/RequestAdditions.ts @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { RESTResponse } from './route-tools/RESTResponse'; -import { AccountEntity } from './Entities/AccountEntity'; -import { DomainEntity } from './Entities/DomainEntity'; -import { PlaceEntity } from './Entities/PlaceEntity'; -import { SessionEntity } from './Entities/SessionEntity'; -import { AuthToken } from '@Entities/AuthToken'; +import { RESTResponse } from "./route-tools/RESTResponse"; +import { AccountEntity } from "./Entities/AccountEntity"; +import { DomainEntity } from "./Entities/DomainEntity"; +import { PlaceEntity } from "./Entities/PlaceEntity"; +import { SessionEntity } from "./Entities/SessionEntity"; +import { AuthToken } from "@Entities/AuthToken"; // This defintion adds our parameters to the Request type // Most of these are variables that are filled by processing on the request @@ -45,4 +44,4 @@ declare global { vParam3?: string; // Used by maint for parameters } } -}; +} diff --git a/src/Tools/Db.ts b/src/Tools/Db.ts index 17dee316..6a815610 100755 --- a/src/Tools/Db.ts +++ b/src/Tools/Db.ts @@ -1,307 +1,311 @@ -// Copyright 2020 Vircadia Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -'use strict' - -import { Config } from '@Base/config'; - -import { MongoClient, Db, FilterQuery } from 'mongodb'; - -import deepmerge from 'deepmerge'; - -// This seems to create a circular reference that causes variables to not be initialized -// import { domainCollection } from '@Entities/Domains'; - -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; - -import { VKeyedCollection } from '@Tools/vTypes'; -import { SimpleObject, IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; - -// The initial MongoClient -export let BaseClient: MongoClient; -// The base database for this application (spec'ed in Config.database.db) -export let Datab: Db; - -// Do the setup of the database. -// Return a Promise for the creation operation that resolved with the MongoClient. -// The operation initializes the global 'BaseClient' and 'Datab' with the latter -// being the configured database to use with this application (spec'ed in -// Config.database.db). -export async function setupDB(): Promise { - // Connection URL docs: https://docs.mongodb.com/manual/reference/connection-string/ - let connectUrl: string; - if (IsNotNullOrEmpty(Config.database["db-connection"])) { - // if user supplied a connection string, just use that - connectUrl = Config.database["db-connection"]; - Logger.debug(`setupDb: using user supplied connection string`); - } - else { - const userSpec = `${Config.database["db-user"]}:${Config.database["db-pw"]}`; - const hostSpec = `${Config.database["db-host"]}:${Config.database["db-port"]}`; - let optionsSpec = ''; - if (Config.database["db-authdb"] !== 'admin') { - optionsSpec += `?authSource=${Config.database["db-authdb"]}`; - }; - connectUrl = `mongodb://${userSpec}@${hostSpec}/${optionsSpec}`; - Logger.debug(`setupDb: connecting to DB at: mongodb://USER@${hostSpec}/${optionsSpec}`); - }; - - // Connect to the client and then get a database handle to the database to use for this app - // This app returns the base client and keeps it in the exported global variable. - BaseClient = await MongoClient.connect(connectUrl, { - useUnifiedTopology: true, - useNewUrlParser: true - }); - Datab = BaseClient.db(Config.database.db); - - // Do any operations to update database formats - await DoDatabaseFormatChanges(); - - await BuildIndexes(); - - return; -}; - -// Low level access to database to create an object is specified collection. -// Note: there is no check for duplicates. -// Throws exception if anything wrong with the fetch. -export async function createObject(pCollection: string, pObject: any): Promise { - return Datab.collection(pCollection) - .insertOne(pObject, { - forceServerObjectId: true // have server assign _Id rather than the driver - } ); -}; - -// Low level access to database to fetch the first instance of an object matching the criteria -// Throws exception if anything wrong with the fetch. -// You can optionally pass a collation which is used to select index (usually for case-insensitive queries).. -export async function getObject(pCollection: string, - pCriteria: CriteriaFilter, - pCollation?: any): Promise { - if (pCollation) { - Logger.cdebug('db-query-detail', `Db.getObject: collection=${pCollection}, criteria=${JSON.stringify(pCriteria.criteriaParameters())}`); - const cursor = Datab.collection(pCollection) - .find(pCriteria.criteriaParameters()) - .collation(pCollation); - if (await cursor.hasNext()) { - return cursor.next(); - }; - return null; - }; - Logger.cdebug('db-query-detail', `Db.getObject: collection=${pCollection}, criteria=${JSON.stringify(pCriteria.criteriaParameters())}`); - return Datab.collection(pCollection) - .findOne(pCriteria.criteriaParameters()); -}; - -// Low level access to database to update the passed object in the passed collection. -// Note that the passed source object has only the fields to update in the target object. -// Throws exception if anything wrong with the fetch. -// Returns the complete updated object. -export async function updateObjectFields(pCollection: string, - pCriteria: CriteriaFilter, - pFields: VKeyedCollection): Promise { - let doSet = false; - let doUnset = false; - const set: VKeyedCollection = {}; - const unset: VKeyedCollection = {}; - const op: VKeyedCollection = {}; - - // If not updating anything, just return - if (IsNullOrEmpty(pFields) || Object.keys(pFields).length === 0) { - return; - }; - - // Go through all the passed values that are to be updated in the target object - for (const [key, value] of Object.entries(pFields)) { - if (key === 'id') continue; // never change an id - if (value === null) { - doUnset = true; - unset[key] = ''; - } - else { - doSet = true; - set[key] = value - }; - }; - - // Create the 'operation' object that specifies what to change on the target object - if (doSet) { - op.$set = set - }; - if (doUnset) { - op.$unset = unset - }; - - Logger.cdebug('db-query-detail', `Db.updateObjectFields: collection=${pCollection}, criteria=${JSON.stringify(pCriteria.criteriaParameters())}, op=${JSON.stringify(op)}`); - return Datab.collection(pCollection) - .findOneAndUpdate(pCriteria.criteriaParameters(), op, { - returnOriginal: false // return the updated entity - } ); -}; - -export async function deleteMany(pCollection: string, pCriteria: CriteriaFilter): Promise { - const result = await Datab.collection(pCollection).deleteMany(pCriteria.criteriaParameters()); - return result.deletedCount ?? 0; -}; - -export async function deleteOne(pCollection: string, pCriteria: CriteriaFilter): Promise { - let ret = false; - const result = await Datab.collection(pCollection).deleteOne( pCriteria.criteriaParameters() ); - if ( result.result && result.result.ok) { - ret = result.result.ok === 1 - } - else { - ret = (result.deletedCount ?? 0) > 0; - }; - return ret; -}; - -// Return a count of the documents that match the passed filter -export async function countObjects(pCollection: string, - pFilter: CriteriaFilter): Promise { - Logger.cdebug('db-query-detail', `Db.countObjects: collection=${pCollection}, criteria=${JSON.stringify(pFilter.criteriaParameters())}`); - return Datab.collection(pCollection).countDocuments(pFilter.criteriaParameters() as FilterQuery); -}; - -// Low level generator to a stream of objects fitting a criteria. -// Page number starts at 1. -// Throws exception if anything wrong with the fetch. -export async function *getObjects(pCollection: string, - pPager?: CriteriaFilter, - pInfoer?: CriteriaFilter, - pScoper?: CriteriaFilter): AsyncGenerator { - - // If a paging filter is passed, incorporate it's search criteria - let criteria:any = {}; - let sortCriteria:any - - if (pPager) { - criteria = deepmerge(criteria, pPager.criteriaParameters()); - if (pPager.sortCriteriaParameters()) { - sortCriteria = deepmerge(sortCriteria ?? {}, pPager.sortCriteriaParameters()); - }; - }; - if (pInfoer) { - criteria = deepmerge(criteria, pInfoer.criteriaParameters()); - if (pInfoer.sortCriteriaParameters()) { - sortCriteria = deepmerge(sortCriteria ?? {}, pInfoer.sortCriteriaParameters()); - }; - }; - if (pScoper) { - criteria = deepmerge(criteria, pScoper.criteriaParameters()); - if (pScoper.sortCriteriaParameters()) { - sortCriteria = deepmerge(sortCriteria ?? {}, pScoper.sortCriteriaParameters()); - }; - }; - - Logger.cdebug('db-query-detail', `Db.getObjects: collection=${pCollection}, criteria=${JSON.stringify(criteria)}`); - const cursor = Datab.collection(pCollection).find(criteria); - - if (sortCriteria) { - Logger.cdebug('db-query-detail', `Db.getObjects: collection=${pCollection}, sortCriteria=${JSON.stringify(sortCriteria)}`); - cursor.sort(sortCriteria); - }; - - while (await cursor.hasNext()) { - const nextItem = await cursor.next(); - if ( ( pPager ? pPager.criteriaTest(nextItem) : true) - && ( pInfoer ? pInfoer.criteriaTest(nextItem) : true) - && ( pScoper ? pScoper.criteriaTest(nextItem) : true) - ) { - yield nextItem; - }; - }; -}; - -// These definitions are here because doing the 'import' reference seems -// to create some reference loop that causes the field tables in the -// Entities to not get initialized. -const domainCollection = 'domains'; -const accountCollection = 'accounts'; -const placeCollection = 'places'; -const tokenCollection = 'tokens'; - -export let noCaseCollation: any = { - locale: 'en_US', - strength: 2 -}; - -async function BuildIndexes() { - // Accounts: - // 'accountId' - // 'username': should be case-less compare. Also update Accounts.getAccountWithUsername() - // 'locationNodeId' - // 'email' - await Datab.createIndex(accountCollection, { 'id': 1 } ); - await Datab.createIndex(accountCollection, { 'username': 1 }, - { collation: noCaseCollation } ); - await Datab.createIndex(accountCollection, { 'locationNodeId': 1 } ); - await Datab.createIndex(accountCollection, { 'email': 1 }, - { collation: noCaseCollation } ); - // Domains: - // 'domainId' - // 'apiKey' - // 'lastSenderKey' - await Datab.createIndex(domainCollection, { 'id': 1 } ); - await Datab.createIndex(domainCollection, { 'apiKey': 1 } ); - await Datab.createIndex(domainCollection, { 'lastSenderKey': 1 } ); - // Places: - // 'placeId' - // 'name' - await Datab.createIndex(placeCollection, { 'id': 1 } ); - await Datab.createIndex(placeCollection, { 'name': 1 }, - { collation: { locale: 'en_US', strength: 2 } } ); -}; - -// Do any database format changes. -// Eventually, there should be a system of multiple, versioned updates -// but, to keep things running, just do the updates needed for now. -async function DoDatabaseFormatChanges() { - - await RenameDbField(domainCollection, 'domainId', 'id'); - // Domain naming changed a little when place_names were added. - // so domain.placeName changed to domain.name. - // If any domain entry exists with 'place_name', replace it with 'name' - await RenameDbField(domainCollection, 'placeName', 'name'); - - await RenameDbField(domainCollection, 'sponserAccountId', 'sponsorAccountId'); - await RenameDbField(domainCollection, 'whenDomainEntryCreated', 'whenCreated'); - - await RenameDbField(accountCollection, 'accountId', 'id'); - await RenameDbField(accountCollection, 'whenAccountCreated', 'whenCreated'); - - await RenameDbField(placeCollection, 'placeId', 'id'); - await RenameDbField(placeCollection, 'address', 'path'); - await RenameDbField(placeCollection, 'whenPlaceEntryCreated', 'whenCreated'); - - await RenameDbField(tokenCollection, 'tokenId', 'id'); - await RenameDbField(tokenCollection, 'tokenExpirationTime', 'expirationTime'); - await RenameDbField(tokenCollection, 'tokenCreationTime', 'whenCreated'); -}; - -// Scan the collection for entities with the 'from' field. Rename to 'to' if found. -async function RenameDbField(pCollection: string, pFrom: string, pTo: string) { - let updateCount = 0; - await Datab.collection(pCollection).find(SimpleObject(pFrom, { '$exists': true })) - .forEach( doc => { - updateCount++; - Datab.collection(pCollection).updateOne( - { _id: doc._id}, - { '$rename': SimpleObject(pFrom, pTo) } - ); - }); - if (updateCount > 0) { - Logger.debug(`Db.DoDatabaseFormatChanges: ${updateCount} ${pCollection}.${pFrom} renames`); - }; -}; \ No newline at end of file +// Copyright 2020 Vircadia Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import { Config } from "@Base/config"; + +import { MongoClient, Db, FilterQuery } from "mongodb"; + +import deepmerge from "deepmerge"; + +// This seems to create a circular reference that causes variables to not be initialized +// import { domainCollection } from '@Entities/Domains'; + +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; + +import { VKeyedCollection } from "@Tools/vTypes"; +import { SimpleObject, IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; + +// The initial MongoClient +export let BaseClient: MongoClient; +// The base database for this application (spec'ed in Config.database.db) +export let Datab: Db; + +// Do the setup of the database. +// Return a Promise for the creation operation that resolved with the MongoClient. +// The operation initializes the global 'BaseClient' and 'Datab' with the latter +// being the configured database to use with this application (spec'ed in +// Config.database.db). +export async function setupDB(): Promise { + // Connection URL docs: https://docs.mongodb.com/manual/reference/connection-string/ + let connectUrl: string; + if (IsNotNullOrEmpty(Config.database["db-connection"])) { + // if user supplied a connection string, just use that + connectUrl = Config.database["db-connection"]; + Logger.debug(`setupDb: using user supplied connection string`); + } else { + const userSpec = `${Config.database["db-user"]}:${Config.database["db-pw"]}`; + const hostSpec = `${Config.database["db-host"]}:${Config.database["db-port"]}`; + let optionsSpec = ""; + if (Config.database["db-authdb"] !== "admin") { + optionsSpec += `?authSource=${Config.database["db-authdb"]}`; + } + connectUrl = `mongodb://${userSpec}@${hostSpec}/${optionsSpec}`; + Logger.debug(`setupDb: connecting to DB at: mongodb://USER@${hostSpec}/${optionsSpec}`); + } + + // Connect to the client and then get a database handle to the database to use for this app + // This app returns the base client and keeps it in the exported global variable. + BaseClient = await MongoClient.connect(connectUrl, { + useUnifiedTopology: true, + useNewUrlParser: true + }); + Datab = BaseClient.db(Config.database.db); + + // Do any operations to update database formats + await DoDatabaseFormatChanges(); + + await BuildIndexes(); + + +} + +// NOTE: Use this only for testing. Directly sets the database global to another value. +export async function setDB(pDb: Db) { + Datab = pDb; +} + +// Low level access to database to create an object is specified collection. +// Note: there is no check for duplicates. +// Throws exception if anything wrong with the fetch. +export async function createObject(pCollection: string, pObject: any): Promise { + return Datab.collection(pCollection) + .insertOne(pObject, { + forceServerObjectId: true // have server assign _Id rather than the driver + }); +} + +// Low level access to database to fetch the first instance of an object matching the criteria +// Throws exception if anything wrong with the fetch. +// You can optionally pass a collation which is used to select index (usually for case-insensitive queries).. +export async function getObject(pCollection: string, + pCriteria: CriteriaFilter, + pCollation?: any): Promise { + if (pCollation) { + Logger.cdebug("db-query-detail", `Db.getObject: collection=${pCollection}, criteria=${JSON.stringify(pCriteria.criteriaParameters())}`); + const cursor = Datab.collection(pCollection) + .find(pCriteria.criteriaParameters()) + .collation(pCollation); + if (await cursor.hasNext()) { + return cursor.next(); + } + return null; + } + Logger.cdebug("db-query-detail", `Db.getObject: collection=${pCollection}, criteria=${JSON.stringify(pCriteria.criteriaParameters())}`); + return Datab.collection(pCollection) + .findOne(pCriteria.criteriaParameters()); +} + +// Low level access to database to update the passed object in the passed collection. +// Note that the passed source object has only the fields to update in the target object. +// Throws exception if anything wrong with the fetch. +// Returns the complete updated object. +export async function updateObjectFields(pCollection: string, + pCriteria: CriteriaFilter, + pFields: VKeyedCollection): Promise { + let doSet = false; + let doUnset = false; + const set: VKeyedCollection = {}; + const unset: VKeyedCollection = {}; + const op: VKeyedCollection = {}; + + // If not updating anything, just return + if (IsNullOrEmpty(pFields) || Object.keys(pFields).length === 0) { + return; + } + + // Go through all the passed values that are to be updated in the target object + for (const [key, value] of Object.entries(pFields)) { + if (key === "id") { + continue; + } // never change an id + if (value === null) { + doUnset = true; + unset[key] = ""; + } else { + doSet = true; + set[key] = value; + } + } + + // Create the 'operation' object that specifies what to change on the target object + if (doSet) { + op.$set = set; + } + if (doUnset) { + op.$unset = unset; + } + + Logger.cdebug("db-query-detail", `Db.updateObjectFields: collection=${pCollection}, criteria=${JSON.stringify(pCriteria.criteriaParameters())}, op=${JSON.stringify(op)}`); + return Datab.collection(pCollection) + .findOneAndUpdate(pCriteria.criteriaParameters(), op, { + returnOriginal: false // return the updated entity + }); +} + +export async function deleteMany(pCollection: string, pCriteria: CriteriaFilter): Promise { + const result = await Datab.collection(pCollection).deleteMany(pCriteria.criteriaParameters()); + return result.deletedCount ?? 0; +} + +export async function deleteOne(pCollection: string, pCriteria: CriteriaFilter): Promise { + let ret = false; + const result = await Datab.collection(pCollection).deleteOne(pCriteria.criteriaParameters()); + if (result.result && result.result.ok) { + ret = result.result.ok === 1; + } else { + ret = (result.deletedCount ?? 0) > 0; + } + return ret; +} + +// Return a count of the documents that match the passed filter +export async function countObjects(pCollection: string, + pFilter: CriteriaFilter): Promise { + Logger.cdebug("db-query-detail", `Db.countObjects: collection=${pCollection}, criteria=${JSON.stringify(pFilter.criteriaParameters())}`); + return Datab.collection(pCollection).countDocuments(pFilter.criteriaParameters() as FilterQuery); +} + +// Low level generator to a stream of objects fitting a criteria. +// Page number starts at 1. +// Throws exception if anything wrong with the fetch. +export async function *getObjects(pCollection: string, + pPager?: CriteriaFilter, + pInfoer?: CriteriaFilter, + pScoper?: CriteriaFilter): AsyncGenerator { + + // If a paging filter is passed, incorporate it's search criteria + let criteria: any = {}; + let sortCriteria: any; + + if (pPager) { + criteria = deepmerge(criteria, pPager.criteriaParameters()); + if (pPager.sortCriteriaParameters()) { + sortCriteria = deepmerge(sortCriteria ?? {}, pPager.sortCriteriaParameters()); + } + } + if (pInfoer) { + criteria = deepmerge(criteria, pInfoer.criteriaParameters()); + if (pInfoer.sortCriteriaParameters()) { + sortCriteria = deepmerge(sortCriteria ?? {}, pInfoer.sortCriteriaParameters()); + } + } + if (pScoper) { + criteria = deepmerge(criteria, pScoper.criteriaParameters()); + if (pScoper.sortCriteriaParameters()) { + sortCriteria = deepmerge(sortCriteria ?? {}, pScoper.sortCriteriaParameters()); + } + } + + Logger.cdebug("db-query-detail", `Db.getObjects: collection=${pCollection}, criteria=${JSON.stringify(criteria)}`); + const cursor = Datab.collection(pCollection).find(criteria); + + if (sortCriteria) { + Logger.cdebug("db-query-detail", `Db.getObjects: collection=${pCollection}, sortCriteria=${JSON.stringify(sortCriteria)}`); + cursor.sort(sortCriteria); + } + + while (await cursor.hasNext()) { + const nextItem = await cursor.next(); + if ((pPager ? pPager.criteriaTest(nextItem) : true) + && (pInfoer ? pInfoer.criteriaTest(nextItem) : true) + && (pScoper ? pScoper.criteriaTest(nextItem) : true) + ) { + yield nextItem; + } + } +} + +// These definitions are here because doing the 'import' reference seems +// to create some reference loop that causes the field tables in the +// Entities to not get initialized. +const domainCollection = "domains"; +const accountCollection = "accounts"; +const placeCollection = "places"; +const tokenCollection = "tokens"; + +export const noCaseCollation: any = { + locale: "en_US", + strength: 2 +}; + +async function BuildIndexes() { + // Accounts: + // 'accountId' + // 'username': should be case-less compare. Also update Accounts.getAccountWithUsername() + // 'locationNodeId' + // 'email' + await Datab.createIndex(accountCollection, { "id": 1 }); + await Datab.createIndex(accountCollection, { "username": 1 }, + { collation: noCaseCollation }); + await Datab.createIndex(accountCollection, { "locationNodeId": 1 }); + await Datab.createIndex(accountCollection, { "email": 1 }, + { collation: noCaseCollation }); + // Domains: + // 'domainId' + // 'apiKey' + // 'lastSenderKey' + await Datab.createIndex(domainCollection, { "id": 1 }); + await Datab.createIndex(domainCollection, { "apiKey": 1 }); + await Datab.createIndex(domainCollection, { "lastSenderKey": 1 }); + // Places: + // 'placeId' + // 'name' + await Datab.createIndex(placeCollection, { "id": 1 }); + await Datab.createIndex(placeCollection, { "name": 1 }, + { collation: { locale: "en_US", strength: 2 } }); +} + +// Do any database format changes. +// Eventually, there should be a system of multiple, versioned updates +// but, to keep things running, just do the updates needed for now. +async function DoDatabaseFormatChanges() { + + await RenameDbField(domainCollection, "domainId", "id"); + // Domain naming changed a little when place_names were added. + // so domain.placeName changed to domain.name. + // If any domain entry exists with 'place_name', replace it with 'name' + await RenameDbField(domainCollection, "placeName", "name"); + + await RenameDbField(domainCollection, "sponserAccountId", "sponsorAccountId"); + await RenameDbField(domainCollection, "whenDomainEntryCreated", "whenCreated"); + + await RenameDbField(accountCollection, "accountId", "id"); + await RenameDbField(accountCollection, "whenAccountCreated", "whenCreated"); + + await RenameDbField(placeCollection, "placeId", "id"); + await RenameDbField(placeCollection, "address", "path"); + await RenameDbField(placeCollection, "whenPlaceEntryCreated", "whenCreated"); + + await RenameDbField(tokenCollection, "tokenId", "id"); + await RenameDbField(tokenCollection, "tokenExpirationTime", "expirationTime"); + await RenameDbField(tokenCollection, "tokenCreationTime", "whenCreated"); +} + +// Scan the collection for entities with the 'from' field. Rename to 'to' if found. +async function RenameDbField(pCollection: string, pFrom: string, pTo: string) { + let updateCount = 0; + await Datab.collection(pCollection).find(SimpleObject(pFrom, { "$exists": true })) + .forEach((doc) => { + updateCount++; + Datab.collection(pCollection).updateOne( + { _id: doc._id }, + { "$rename": SimpleObject(pFrom, pTo) } + ); + }); + if (updateCount > 0) { + Logger.debug(`Db.DoDatabaseFormatChanges: ${updateCount} ${pCollection}.${pFrom} renames`); + } +} diff --git a/src/Tools/Logging.ts b/src/Tools/Logging.ts index c4145b6c..79d37593 100755 --- a/src/Tools/Logging.ts +++ b/src/Tools/Logging.ts @@ -11,132 +11,133 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; -import fs from 'fs'; +import { Config } from "@Base/config"; -import http from 'http'; -import winston, { transports } from 'winston'; -import { Options } from 'morgan'; +import fs from "fs"; + +import http from "http"; +import winston, { transports } from "winston"; +import { Options } from "morgan"; interface ALogger { - info( msg: string ): void, - warn( msg: string ): void, - debug( msg: string ): void, - cdebug( flag: string, msg: string ): void, - error( msg: string ): void, - setLogLevel( level: string ): void -}; + info(msg: string): void, + warn(msg: string): void, + debug(msg: string): void, + cdebug(flag: string, msg: string): void, + error(msg: string): void, + setLogLevel(level: string): void +} // This is an initial logger that exists before configuration is complete. // The later 'initializeLogger' will reset the logger to be to files or whatever. -export let logger = winston.createLogger( { - 'level': 'debug', - 'format': winston.format.combine( - winston.format.timestamp(), - winston.format.printf(info => { return `${info.timestamp} ${info.level} ${info.message}` }) - ), - 'transports': [ - new winston.transports.Console( { - 'level': 'debug' - }) - ] +export let logger = winston.createLogger({ + "level": "debug", + "format": winston.format.combine( + winston.format.timestamp(), + winston.format.printf((info) => { + return `${info.timestamp} ${info.level} ${info.message}`; + }) + ), + "transports": [ + new winston.transports.Console({ + "level": "debug" + }) + ] }); // Switch the logger from the startup console logger to the file logger. export function initLogging() { - // Create the logging directory if it doesn't exist - let logDir = Config.debug["log-directory"]; - if (!fs.existsSync(logDir)) { - try { - fs.mkdirSync(logDir); - } - catch (e) { - logger.error(`Logger: could not create log directory "${logDir}": ${e}`); - logDir = '.'; + // Create the logging directory if it doesn't exist + let logDir = Config.debug["log-directory"]; + if (!fs.existsSync(logDir)) { + try { + fs.mkdirSync(logDir); + } catch (e) { + logger.error(`Logger: could not create log directory "${logDir}": ${e}`); + logDir = "."; + } } - }; - // Create the ways we're going to log. Files and/or console - const logTransports: any[] = []; - if (Config.debug["log-to-files"]) { - logTransports.push( - new winston.transports.File( { - 'level': Config.debug.loglevel, - 'filename': Config.debug['log-filename'], - 'dirname': logDir, - 'maxsize': Config.debug['log-max-size-megabytes']*1000000, // max size in bytes - 'maxFiles': Config.debug['log-max-files'], // number of files to keep - 'tailable': Config.debug["log-tailable"], - 'zippedArchive': Config.debug["log-compress"] // ZIP the previous files - }) - ); - }; - if (Config.debug["log-to-console"]) { - logTransports.push( - new winston.transports.Console( { - 'level': Config.debug.loglevel - }) - ); - }; + // Create the ways we're going to log. Files and/or console + const logTransports: any[] = []; + if (Config.debug["log-to-files"]) { + logTransports.push( + new winston.transports.File({ + "level": Config.debug.loglevel, + "filename": Config.debug["log-filename"], + "dirname": logDir, + "maxsize": Config.debug["log-max-size-megabytes"] * 1000000, // max size in bytes + "maxFiles": Config.debug["log-max-files"], // number of files to keep + "tailable": Config.debug["log-tailable"], + "zippedArchive": Config.debug["log-compress"] // ZIP the previous files + }) + ); + } + if (Config.debug["log-to-console"]) { + logTransports.push( + new winston.transports.Console({ + "level": Config.debug.loglevel + }) + ); + } - // Create the Winston logger - logger = winston.createLogger( { - 'level': Config.debug.loglevel, - 'format': winston.format.combine( - winston.format.timestamp(), - winston.format.printf(info => { return `${info.timestamp} ${info.level} ${info.message}` }) - ), - 'transports': logTransports, - }); -}; + // Create the Winston logger + logger = winston.createLogger({ + "level": Config.debug.loglevel, + "format": winston.format.combine( + winston.format.timestamp(), + winston.format.printf((info) => { + return `${info.timestamp} ${info.level} ${info.message}`; + }) + ), + "transports": logTransports + }); +} -export const Logger : ALogger = { - info: (msg: string) => { - logger.log('info', msg); - }, - warn: (msg: string) => { - logger.log('warn', msg); - }, - debug: (msg: string) => { - logger.log('debug', msg); - }, - // Conditional debug. Looks for "debug.flag" in configuration. - cdebug: (flag: string, msg: string) => { - if ((Config.debug as any)[flag]) { - logger.log('debug', msg); - }; - }, - error: (msg: string) => { - logger.log('error', msg); - }, - setLogLevel: ( level: string) => { - if (level) { - const aLevel = level.toLowerCase(); - if ([ 'info', 'warn', 'debug', 'error'].includes(aLevel)) { - transports.File.level = aLevel; - transports.Console.level = aLevel; - } - else { - Logger.error(`Logger.setLogLevel: unknown level name: ${level}`); - Logger.setLogLevel('info'); - }; +export const Logger: ALogger = { + info: (msg: string) => { + logger.log("info", msg); + }, + warn: (msg: string) => { + logger.log("warn", msg); + }, + debug: (msg: string) => { + logger.log("debug", msg); + }, + // Conditional debug. Looks for "debug.flag" in configuration. + cdebug: (flag: string, msg: string) => { + if ((Config.debug as any)[flag]) { + logger.log("debug", msg); + } + }, + error: (msg: string) => { + logger.log("error", msg); + }, + setLogLevel: (level: string) => { + if (level) { + const aLevel = level.toLowerCase(); + if (["info", "warn", "debug", "error"].includes(aLevel)) { + transports.File.level = aLevel; + transports.Console.level = aLevel; + } else { + Logger.error(`Logger.setLogLevel: unknown level name: ${level}`); + Logger.setLogLevel("info"); + } + } else { + Logger.error(`Logger.setLogLevel: false level name: ${level}`); + transports.File.level = "debug"; + } } - else { - Logger.error(`Logger.setLogLevel: false level name: ${level}`); - transports.File.level = 'debug'; - }; - } }; // The morgan logger for ExpressJS logs the API requests made. // This adds a stream processor so it will log to the above configured logger. export const morganOptions: Options = { - stream: { - write (msg: string) { - Logger.debug(msg); + stream: { + write(msg: string) { + Logger.debug(msg); + } } - } }; diff --git a/src/Tools/Misc.ts b/src/Tools/Misc.ts index 3bb4bf2e..7bc887b7 100755 --- a/src/Tools/Misc.ts +++ b/src/Tools/Misc.ts @@ -11,180 +11,190 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import http from 'http'; -import https from 'https'; -import os from 'os'; -import path from 'path'; -import { v4 as uuidv4 } from 'uuid'; -import crypto from 'crypto'; -import fsPromises from 'fs/promises'; +import http from "http"; +import https from "https"; +import os from "os"; +import path from "path"; +import { v4 as uuidv4 } from "uuid"; +import crypto from "crypto"; -import { createTransport } from 'nodemailer'; +import fsPromises from "fs/promises"; -import { Logger } from '@Tools/Logging'; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Config } from '@Base/config'; -import { AccountEntity } from '@Entities/AccountEntity'; +import { createTransport } from "nodemailer"; + +import { Logger } from "@Tools/Logging"; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Config } from "@Base/config"; +import { AccountEntity } from "@Entities/AccountEntity"; // Clamp the passed value between a high and low export function Clamp(pVal: number, pLow: number, pHigh: number): number { - let ret = pVal; - if (ret > pHigh) ret = pHigh; - if (ret < pLow) ret = pLow; - return ret; + let ret = pVal; + if (ret > pHigh) { + ret = pHigh; + } + if (ret < pLow) { + ret = pLow; + } + return ret; } // Return 'true' if the passed value is null or empty export function IsNullOrEmpty(pVal: any): boolean { - return (typeof(pVal) === 'undefined') - || (pVal === null) - || (typeof(pVal) === 'string' && String(pVal).length === 0); -}; + return typeof pVal === "undefined" + || pVal === null + || typeof pVal === "string" && String(pVal).length === 0; +} // Return 'true' if the passed value is not null or empty export function IsNotNullOrEmpty(pVal: any): boolean { - return !IsNullOrEmpty(pVal); -}; + return !IsNullOrEmpty(pVal); +} // Create an object with the passed property value. // This is often used to make queries where the property name is a value string. export function SimpleObject(pProperty: string, pValue: any): VKeyedCollection { - const ret:VKeyedCollection = {}; - ret[pProperty] = pValue; - return ret; -}; + const ret: VKeyedCollection = {}; + ret[pProperty] = pValue; + return ret; +} // Take apart an URL query string and return an object of key/value pairs -export function ParseQueryString(pQuery: string): Map { - const ret = new Map(); - const args = decodeURI(pQuery).split('&'); - args.forEach( arg => { - const argPieces = arg.split('='); - switch (argPieces.length) { - case 1: - ret.set(argPieces[0], null); break; - case 2: - ret.set(argPieces[0], argPieces[1]); break; - default: - break; // doesn't make sense so ignore it - }; - }) - return ret; -}; +export function ParseQueryString(pQuery: string): Map { + const ret = new Map(); + const args = decodeURI(pQuery).split("&"); + args.forEach((arg) => { + const argPieces = arg.split("="); + switch (argPieces.length) { + case 1: + ret.set(argPieces[0], null); break; + case 2: + ret.set(argPieces[0], argPieces[1]); break; + default: + break; // doesn't make sense so ignore it + } + }); + return ret; +} export function GenUUID(): string { - return uuidv4(); -}; + return uuidv4(); +} // Return a string of random hex numbers of the specified length -export function genRandomString(pLen: number) : string { - return crypto.randomBytes(Math.ceil(pLen/2)) - .toString('hex') - .slice(0, pLen); -}; +export function genRandomString(pLen: number): string { + return crypto.randomBytes(Math.ceil(pLen / 2)) + .toString("hex") + .slice(0, pLen); +} let myExternalAddr: string; export async function getMyExternalIPAddress(): Promise { - if (IsNotNullOrEmpty(myExternalAddr)) { - return Promise.resolve(myExternalAddr); - }; - return new Promise(( resolve, reject) => { - httpsRequest('https://api.ipify.org') - .then( resp => { - myExternalAddr = resp; - resolve(myExternalAddr); - }) - .catch ( err => { - // Can't get it that way for some reason. Ask our interface - const networkInterfaces = os.networkInterfaces(); - // { 'lo1': [ info, info ], 'eth0': [ info, info ]} where 'info' could be v4 and v6 addr infos - - let ret = () => [].concat(...Object.values(networkInterfaces)) - .filter(details => details.family === 'IPv4' && !details.internal) - .pop().address; - if (IsNullOrEmpty(ret)) { - ret = () => [].concat(...Object.values(networkInterfaces)) - .filter(details => details.family === 'IPv6' && !details.internal) - .pop().address; - } - Logger.debug(`getMyExternalIPAddress: resolved interface addr = ${ret}`); - if (IsNullOrEmpty(ret)) { - reject('No address found'); - } - myExternalAddr = ret.toString(); - resolve(myExternalAddr); + if (IsNotNullOrEmpty(myExternalAddr)) { + return Promise.resolve(myExternalAddr); + } + return new Promise((resolve, reject) => { + httpsRequest("https://api.ipify.org") + .then((resp) => { + myExternalAddr = resp; + resolve(myExternalAddr); + }) + ["catch"]((err) => { + // Can't get it that way for some reason. Ask our interface + const networkInterfaces = os.networkInterfaces(); + // { 'lo1': [ info, info ], 'eth0': [ info, info ]} where 'info' could be v4 and v6 addr infos + + let ret = () => { + return [].concat(...Object.values(networkInterfaces)) + .filter((details) => { + return details.family === "IPv4" && !details.internal; + }) + .pop().address; + }; + if (IsNullOrEmpty(ret)) { + ret = () => { + return [].concat(...Object.values(networkInterfaces)) + .filter((details) => { + return details.family === "IPv6" && !details.internal; + }) + .pop().address; + }; + } + Logger.debug(`getMyExternalIPAddress: resolved interface addr = ${ret}`); + if (IsNullOrEmpty(ret)) { + reject("No address found"); + } + myExternalAddr = ret.toString(); + resolve(myExternalAddr); + }); }); - }); -}; +} // Do a simple http GET and return the response as a string export async function httpRequest(pUrl: string): Promise { - return new Promise( ( resolve, reject) => { - http.get(pUrl, (resp) => { - let data = ''; - resp.on('data', (chunk: string) => { - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }) - }).on('error', (err: any) => { - reject(err); + return new Promise((resolve, reject) => { + http.get(pUrl, (resp) => { + let data = ""; + resp.on("data", (chunk: string) => { + data += chunk; + }); + resp.on("end", () => { + resolve(data); + }); + }).on("error", (err: any) => { + reject(err); + }); }); - }); -}; +} // Do a simple https GET and return the response as a string export async function httpsRequest(pUrl: string): Promise { - return new Promise( ( resolve, reject) => { - https.get(pUrl, (resp) => { - let data = ''; - resp.on('data', (chunk: string) => { - data += chunk; - }); - resp.on('end', () => { - resolve(data); - }) - }).on('error', (err: any) => { - reject(err); + return new Promise((resolve, reject) => { + https.get(pUrl, (resp) => { + let data = ""; + resp.on("data", (chunk: string) => { + data += chunk; + }); + resp.on("end", () => { + resolve(data); + }); + }).on("error", (err: any) => { + reject(err); + }); }); - }); -}; +} export async function SendVerificationEmail(pAccount: AccountEntity, pVerifyCode: string): Promise { try { - const verificationURL = Config.metaverse['metaverse-server-url'] + const verificationURL = Config.metaverse["metaverse-server-url"] + `/api/v1/account/verify/email?a=${pAccount.id}&v=${pVerifyCode}`; - const metaverseName = Config.metaverse['metaverse-name']; - const shortMetaverseName = Config.metaverse['metaverse-nick-name']; + const metaverseName = Config.metaverse["metaverse-name"]; + const shortMetaverseName = Config.metaverse["metaverse-nick-name"]; - const verificationFile = path.join(__dirname, '../..', Config['metaverse-server']['email-verification-email-body']); + const verificationFile = path.join(__dirname, "../..", Config["metaverse-server"]["email-verification-email-body"]); Logger.debug(`SendVerificationEmail: using verificationFile from ${verificationFile}`); - let emailBody = await fsPromises.readFile(verificationFile, 'utf-8'); - emailBody = emailBody.replace('VERIFICATION_URL', verificationURL) - .replace('METAVERSE_NAME', metaverseName) - .replace('SHORT_METAVERSE_NAME', shortMetaverseName); + let emailBody = await fsPromises.readFile(verificationFile, "utf-8"); + emailBody = emailBody.replace("VERIFICATION_URL", verificationURL) + .replace("METAVERSE_NAME", metaverseName) + .replace("SHORT_METAVERSE_NAME", shortMetaverseName); - Logger.debug(`SendVerificationEmail: SMTPhost=${Config['nodemailer-transport-config'].host}`); - const transporter = createTransport(Config['nodemailer-transport-config']); + Logger.debug(`SendVerificationEmail: SMTPhost=${Config["nodemailer-transport-config"].host}`); + const transporter = createTransport(Config["nodemailer-transport-config"]); if (transporter) { Logger.debug(`SendVerificationEmail: sending email verification for new account ${pAccount.id}/${pAccount.username}`); const msg = { - from: Config['metaverse-server']['email-verification-from'], + from: Config["metaverse-server"]["email-verification-from"], to: pAccount.email, subject: `${shortMetaverseName} account verification`, html: emailBody }; transporter.sendMail(msg); transporter.close(); - } - else { + } else { Logger.error(`SendVerificationEmail: failed to recreate transporter`); - }; - } - catch (e) { + } + } catch (e) { Logger.error(`SendVerificationEmail: exception sending verification email. Acct=${pAccount.id}/${pAccount.username}. e=${e}`); } - return; + } diff --git a/src/Tools/vTypes.ts b/src/Tools/vTypes.ts index 1f330cee..8e9cedb8 100755 --- a/src/Tools/vTypes.ts +++ b/src/Tools/vTypes.ts @@ -11,51 +11,54 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; + +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; // An object that is used as a keyed collection of objects. // The key is always a string export interface VKeyedCollection { - [ key: string]: any -}; + [ key: string]: any +} export interface VKeyValue { - [ key: string]: string -}; + [ key: string]: string +} // String array. // Several structures are an array of strings (TokenScope, AccountRoles, ...). -export class SArray { - static has(pArray: string[], pCheck: string): boolean { - return IsNullOrEmpty(pArray) ? false : pArray.includes(pCheck); - }; - static hasNoCase(pArray: string[], pCheck: string): boolean { - const pCheckLower = pCheck.toLowerCase(); - if (IsNotNullOrEmpty(pArray)) { - for (const ent of pArray) { - if (ent.toLowerCase() === pCheckLower) { - return true; - }; - } +export class SArray { + static has(pArray: string[], pCheck: string): boolean { + return IsNullOrEmpty(pArray) ? false : pArray.includes(pCheck); + } + + static hasNoCase(pArray: string[], pCheck: string): boolean { + const pCheckLower = pCheck.toLowerCase(); + if (IsNotNullOrEmpty(pArray)) { + for (const ent of pArray) { + if (ent.toLowerCase() === pCheckLower) { + return true; + } + } + } + return false; + } + + static add(pArray: string[], pAdd: string): boolean { + let added = false; + if (typeof pAdd === "string") { + if (!pArray.includes(pAdd)) { + pArray.push(pAdd); + added = true; + } + } + return added; + } + + static remove(pArray: string[], pRemove: string): void { + const idx = pArray.indexOf(pRemove); + if (idx >= 0) { + pArray.splice(idx, 1); + } } - return false; - }; - static add(pArray: string[], pAdd: string): boolean { - let added = false; - if (typeof(pAdd) === 'string') { - if (! pArray.includes(pAdd)) { - pArray.push(pAdd); - added = true; - }; - }; - return added; - }; - static remove(pArray: string[], pRemove: string): void { - const idx = pArray.indexOf(pRemove); - if (idx >= 0) { - pArray.splice(idx, 1); - }; - }; - -}; \ No newline at end of file + +} diff --git a/src/config.ts b/src/config.ts index 794b2f7e..2320157f 100755 --- a/src/config.ts +++ b/src/config.ts @@ -6,157 +6,156 @@ // Do no change this file but instead create a 'server.user-config-file' (default // name of "iamus.json") whos values overlay these default values. -'use strict'; -import fs from 'fs'; +import fs from "fs"; -import deepmerge from 'deepmerge'; +import deepmerge from "deepmerge"; -import { IsNullOrEmpty, IsNotNullOrEmpty, getMyExternalIPAddress } from '@Tools/Misc'; -import { httpRequest, httpsRequest } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { IsNullOrEmpty, IsNotNullOrEmpty, getMyExternalIPAddress } from "@Tools/Misc"; +import { httpRequest, httpsRequest } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // All the possible configuration parameters. // This sets defaults values and is over-written by environment variables and // supplied configuration file contents. export let Config = { // The metaverse identity - 'metaverse': { - 'metaverse-name': 'Vircadia noobie', - 'metaverse-nick-name': 'Noobie', - 'metaverse-server-url': '', // if empty, set to self - 'default-ice-server-url': '', // if empty, set to self - 'dashboard-url': 'https://dashboard.vircadia.com' + "metaverse": { + "metaverse-name": "Vircadia noobie", + "metaverse-nick-name": "Noobie", + "metaverse-server-url": "", // if empty, set to self + "default-ice-server-url": "", // if empty, set to self + "dashboard-url": "https://dashboard.vircadia.com" }, // Server network parameters - 'server': { - 'listen-host': '0.0.0.0', - 'listen-port': 9400, - 'key-file': '', // if supplied, do https - 'cert-file': '', - 'max-body-size': 300000, // maximum body size for input JSON bodies - 'static-base': '/static', // base of static data URL - 'user-config-file': './iamus.json', // startup config over-ride - 'server-version': { // overlaid with VERSION.json - 'version-tag': '1.1.1-20200101-abcdefg' + "server": { + "listen-host": "0.0.0.0", + "listen-port": 9400, + "key-file": "", // if supplied, do https + "cert-file": "", + "max-body-size": 300000, // maximum body size for input JSON bodies + "static-base": "/static", // base of static data URL + "user-config-file": "./iamus.json", // startup config over-ride + "server-version": { // overlaid with VERSION.json + "version-tag": "1.1.1-20200101-abcdefg" } }, // Authorization token parameters - 'auth': { - 'domain-token-expire-hours': 24 * 365, // one year - 'owner-token-expire-hours': 24 * 7 // one week + "auth": { + "domain-token-expire-hours": 24 * 365, // one year + "owner-token-expire-hours": 24 * 7 // one week }, // Control of the metaverse operations - 'metaverse-server': { - 'http-error-on-failure': true, // whether to include x-vircadia error header - 'error-header': 'x-vircadia-error-handle', + "metaverse-server": { + "http-error-on-failure": true, // whether to include x-vircadia error header + "error-header": "x-vircadia-error-handle", - 'metaverse-info-addition-file': './metaverse_info.json', - 'max-name-length': 32, // the max characters a domain, place, account, ... name + "metaverse-info-addition-file": "./metaverse_info.json", + "max-name-length": 32, // the max characters a domain, place, account, ... name - 'session-timeout-minutes': 5, - 'heartbeat-seconds-until-offline': 5 * 60, // seconds until non-heartbeating user is offline - 'domain-seconds-until-offline': 10 * 60, // seconds until non-heartbeating domain is offline - 'domain-seconds-check-if-online': 2 * 60, // how often to check if a domain is online - 'handshake-request-expiration-minutes': 1, // minutes that a handshake friend request is active - 'connection-request-expiration-minutes': 60 * 24 * 4, // 4 days - 'friend-request-expiration-minutes': 60 * 24 * 4, // 4 days + "session-timeout-minutes": 5, + "heartbeat-seconds-until-offline": 5 * 60, // seconds until non-heartbeating user is offline + "domain-seconds-until-offline": 10 * 60, // seconds until non-heartbeating domain is offline + "domain-seconds-check-if-online": 2 * 60, // how often to check if a domain is online + "handshake-request-expiration-minutes": 1, // minutes that a handshake friend request is active + "connection-request-expiration-minutes": 60 * 24 * 4, // 4 days + "friend-request-expiration-minutes": 60 * 24 * 4, // 4 days - 'place-current-timeout-minutes': 5, // minutes until current place info is stale - 'place-inactive-timeout-minutes': 60, // minutes until place is considered inactive - 'place-check-last-activity-seconds': (3*60)-5, // seconds between checks for Place lastActivity updates + "place-current-timeout-minutes": 5, // minutes until current place info is stale + "place-inactive-timeout-minutes": 60, // minutes until place is considered inactive + "place-check-last-activity-seconds": 3 * 60 - 5, // seconds between checks for Place lastActivity updates // redirection URL used for initial domain token generation, // "METAVERSE_SERVER_URL" is replaced (from Config.metaverse.metaverse-server-url) // "DASHBOARD_URL" is replaced (from Config.metaverse.dashboard-url) - 'tokengen_url': 'METAVERSE_SERVER_URL/static/DomainTokenLogin.html', + "tokengen_url": "METAVERSE_SERVER_URL/static/DomainTokenLogin.html", // 'tokengen_url': 'DASHBOARD_URL?metaverse=METAVERSE_SERVER_URL&page=domain', // When account of this name is created, add 'admin' role to it // Initially as empty so random people cannot create an admin account // The account named here MUST be controlled by the server's admin - 'base-admin-account': '', + "base-admin-account": "", // If to assume domain network_address if on is not set - 'fix-domain-network-address': true, + "fix-domain-network-address": true, // Whether allowing temp domain name creation - 'allow-temp-domain-creation': false, + "allow-temp-domain-creation": false, // Email verification on account creation - 'enable-account-email-verification': false, - 'email-verification-timeout-minutes': 1440, // minutes to wait for email verification (1440=one day) + "enable-account-email-verification": false, + "email-verification-timeout-minutes": 1440, // minutes to wait for email verification (1440=one day) // default is in 'static' dir. If you put in 'config' dir, use 'config/verificationEmail.html'. // "VERIFICATION_URL" is replaced with the computed URL (build with Config.metaverse-server-url) // "METAVERSE_NAME" is replaced (from Config.metaverse.metaverse-name) // "SHORT_METAVERSE_NAME" is replaced (from Config.metaverse.metaverse-nick-name) - 'email-verification-email-body': 'dist/static/verificationEmail.html', // file to send - 'email-verification-from': '', // who the email is From + "email-verification-email-body": "dist/static/verificationEmail.html", // file to send + "email-verification-from": "", // who the email is From // When user follows the verification URL, they are redirected to one of these two URLs // "METAVERSE_SERVER_URL" is replaced (from Config.metaverse.metaverse-server-url) // "DASHBOARD_URL" is replaced (from Config.metaverse.dashboard-url) // "ACCOUNT_ID" is replaced with the verifying account id // "FAILURE_REASON" is replaced with the reason for verification failure (url encoded) - 'email-verification-success-redirect': 'METAVERSE_SERVER_URL/static/verificationEmailSuccess.html', - 'email-verification-failure-redirect': 'METAVERSE_SERVER_URL/static/verificationEmailFailure.html?r=FAILURE_REASON' + "email-verification-success-redirect": "METAVERSE_SERVER_URL/static/verificationEmailSuccess.html", + "email-verification-failure-redirect": "METAVERSE_SERVER_URL/static/verificationEmailFailure.html?r=FAILURE_REASON" }, // SMTP mail parameters for out-bound email // This is the structure that is passed to NodeMailer's SMTP transport. // Check out the documentation at https://nodemailer.com/smtp/ // For SMTP outbound, setup your email account on your service and // update SMTP-HOSTNAME, SMTP-USER, and SMTP-PASSWORD with your info. - 'nodemailer-transport-config': { - 'host': 'SMTP-HOSTNAME', - 'port': 465, // 587 if secure=false - 'secure': true, - 'auth': { - 'user': 'SMTP-USER', - 'pass': 'SMTP-PASSWORD' + "nodemailer-transport-config": { + "host": "SMTP-HOSTNAME", + "port": 465, // 587 if secure=false + "secure": true, + "auth": { + "user": "SMTP-USER", + "pass": "SMTP-PASSWORD" } }, - 'monitoring': { - 'enable': true, // enable value monitoring - 'history': true // whether to keep value history + "monitoring": { + "enable": true, // enable value monitoring + "history": true // whether to keep value history }, // Setup for MongoDB access - 'database': { - 'db-host': 'localhost', - 'db-port': 27017, - 'db': 'tester', - 'db-user': 'metaverse', - 'db-pw': 'nooneknowsit', - 'db-authdb': 'admin', - 'db-connection': '' // connection string replaces above if supplied + "database": { + "db-host": "localhost", + "db-port": 27017, + "db": "tester", + "db-user": "metaverse", + "db-pw": "nooneknowsit", + "db-authdb": "admin", + "db-connection": "" // connection string replaces above if supplied }, // MongoDB account configured for database backup script - 'backup': { + "backup": { "backup-user": "backuper", // database backup user account (for BackupDb.sh) "backup-pw": "nooneknowsit", // database backup user password (for BackupDb.sh) "backup-dir": "directoryName", // Backup file directory. Optional. Defaults to "./DatabaseBackup" "authenticationDatabase": "databaseName" // auth db for backup user. Optional. Defaults to "admin" }, - 'debug': { - 'loglevel': 'info', + "debug": { + "loglevel": "info", // Winston logging configuration - 'log-to-files': true, // if to log to files - 'log-filename': 'iamus.log', // filename for log files - 'log-directory': './logs', // directory to place logs - 'log-max-size-megabytes': 100,// max mega-bytes per log file - 'log-max-files': 10, // number of log files to create - 'log-tailable': true, // if to always output to main named log file - 'log-compress': false, // if to compress old log files + "log-to-files": true, // if to log to files + "log-filename": "iamus.log", // filename for log files + "log-directory": "./logs", // directory to place logs + "log-max-size-megabytes": 100, // max mega-bytes per log file + "log-max-files": 10, // number of log files to create + "log-tailable": true, // if to always output to main named log file + "log-compress": false, // if to compress old log files - 'log-to-console': false, // if to additionally log to the console + "log-to-console": false, // if to additionally log to the console - 'devel': false, + "devel": false, // Control of what debug information is logged - 'request-detail': false, // output the received request info when received - 'request-body': false, // output the received request body when received - 'metaverseapi-response-detail': false, // output the response sent back from MetaverseAPI requests - 'query-detail': false, // outputs details when selecting query parameters - 'db-query-detail': false, // outputs details about DB queries - 'field-setting': false // Details of entity field getting and setting + "request-detail": false, // output the received request info when received + "request-body": false, // output the received request body when received + "metaverseapi-response-detail": false, // output the response sent back from MetaverseAPI requests + "query-detail": false, // outputs details when selecting query parameters + "db-query-detail": false, // outputs details about DB queries + "field-setting": false // Details of entity field getting and setting } }; @@ -166,16 +165,24 @@ export async function initializeConfiguration(): Promise { // Tweek some of the values based on environment variables const envLogLevel = process.env.IAMUS_LOGLEVEL; - if (IsNotNullOrEmpty(envLogLevel)) Config.debug.loglevel = envLogLevel; + if (IsNotNullOrEmpty(envLogLevel)) { + Config.debug.loglevel = envLogLevel; + } const envHost = process.env.IAMUS_LISTEN_HOST; - if (IsNotNullOrEmpty(envHost)) Config.server['listen-host'] = envHost; + if (IsNotNullOrEmpty(envHost)) { + Config.server["listen-host"] = envHost; + } const envPort = process.env.IAMUS_LISTEN_PORT; - if (IsNotNullOrEmpty(envPort)) Config.server['listen-port'] = Number(envPort); + if (IsNotNullOrEmpty(envPort)) { + Config.server["listen-port"] = Number(envPort); + } const envConfigFile = process.env.IAMUS_CONFIG_FILE; - if (IsNotNullOrEmpty(envConfigFile)) Config.server["user-config-file"] = envConfigFile; + if (IsNotNullOrEmpty(envConfigFile)) { + Config.server["user-config-file"] = envConfigFile; + } // Read in the configuration file if it exists and overlay the values. try { @@ -187,55 +194,53 @@ export async function initializeConfiguration(): Promise { // this overlays all the Config values with values from the user's file Config = deepmerge(Config, userConfig); // Logger.debug(`initializeConfiguration: processed configuration file ${userConfigFile}`); - }; - }; - } - catch (e) { - Logger.error('initializeConfiguration: exception adding user config: ' + e); + } + } + } catch (e) { + Logger.error("initializeConfiguration: exception adding user config: " + e); } // Read in version info from distribution version file try { let versionInfo: any; // depending on how built, version file might be in different places - for (const versionFile of [ './VERSION.json' , './dist/VERSION.json' ]) { + for (const versionFile of ["./VERSION.json", "./dist/VERSION.json"]) { if (fs.existsSync(versionFile)) { versionInfo = await readInJSON(versionFile); break; - }; - }; + } + } if (IsNullOrEmpty(versionInfo)) { versionInfo = { - 'version-tab': 'unknown' + "version-tab": "unknown" }; - }; + } Config.server["server-version"] = versionInfo; Logger.debug(`initializeConfiguration: version info: ${JSON.stringify(versionInfo, null, 4)}`); + } catch (e) { + Logger.error("initializeConfiguration: exception reading version info: " + e); } - catch (e) { - Logger.error('initializeConfiguration: exception reading version info: ' + e); - }; // If no ice server address is specified, assume ours if (IsNullOrEmpty(Config.metaverse["default-ice-server-url"])) { const myAddr = await getMyExternalIPAddress(); Logger.debug(`initializeConfiguration: made ice server addr of ${myAddr}`); Config.metaverse["default-ice-server-url"] = myAddr; - }; + } // If no external metaverse server url is specified, make one from our address if (IsNullOrEmpty(Config.metaverse["metaverse-server-url"])) { const myAddr = await getMyExternalIPAddress(); const newUrl = `http://${myAddr}:${Config.server["listen-port"].toString()}/`; Logger.debug(`initializeConfiguration: built metaverse url of ${newUrl}`); Config.metaverse["metaverse-server-url"] = newUrl; - }; + } // Make sure the metaverse-server-url does not end in a slash. // There is a bunch of code that expects to add the "/api/v1/..." to this variable. let msu: string = Config.metaverse["metaverse-server-url"]; - while (msu.endsWith('/')) { + while (msu.endsWith("/")) { msu = msu.slice(0, -1); - }; + } Config.metaverse["metaverse-server-url"] = msu; // Write a subset of the built configuration information into the 'static' directory @@ -246,51 +251,45 @@ export async function initializeConfiguration(): Promise { configSubset.debug = Config.debug; // Depending on how started, the static dir can be in different places - const staticBase: string = Config.server['static-base']; - for (const staticDir of [ '.' + staticBase, './dist' + staticBase ]) { + const staticBase: string = Config.server["static-base"]; + for (const staticDir of ["." + staticBase, "./dist" + staticBase]) { if (fs.existsSync(staticDir)) { - const configSubsetFilename = staticDir + '/config.json'; + const configSubsetFilename = staticDir + "/config.json"; try { fs.writeFileSync(configSubsetFilename, JSON.stringify(configSubset)); Logger.info(`initializeConfiguration: wrote static config subset to ${configSubsetFilename}`); - } - catch (err) { + } catch (err) { Logger.error(`initializeConfiguration: error writing ${configSubsetFilename}: ${err}`); - }; + } break; - }; - }; + } + } // Logger.debug(`initializeConfiguration: debug setting: ${JSON.stringify(Config.debug)}`); - return; -}; + +} // Utility routine that reads in JSON content from either an URL or a filename. // Returns the parsed JSON object or 'undefined' if any errors. export async function readInJSON(pFilenameOrURL: string): Promise { let configBody: string; - if (pFilenameOrURL.startsWith('http://')) { + if (pFilenameOrURL.startsWith("http://")) { configBody = await httpRequest(pFilenameOrURL); - } - else { - if (pFilenameOrURL.startsWith('https://')) { - configBody = await httpsRequest(pFilenameOrURL); + } else if (pFilenameOrURL.startsWith("https://")) { + configBody = await httpsRequest(pFilenameOrURL); + } else { + try { + // We should technically sanitize this filename but if one can change the environment + // or config file variables, the app is already poned. + configBody = fs.readFileSync(pFilenameOrURL, "utf-8"); + } catch (err) { + Logger.debug(`readInJSON: failed read of user config file ${pFilenameOrURL}: ${err}`); } - else { - try { - // We should technically sanitize this filename but if one can change the environment - // or config file variables, the app is already poned. - configBody = fs.readFileSync(pFilenameOrURL, 'utf-8'); - } - catch (err) { - Logger.debug(`readInJSON: failed read of user config file ${pFilenameOrURL}: ${err}`); - }; - }; - }; + } if (IsNotNullOrEmpty(configBody)) { return JSON.parse(configBody); - }; + } return undefined; -}; +} export default Config; diff --git a/src/index.ts b/src/index.ts index bfeb01f0..b7ccc928 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,224 +1,227 @@ -// Copyright 2020 Vircadia Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -'use strict' - -import 'module-alias/register'; - -import { Config, initializeConfiguration } from '@Base/config'; - -import http from 'http'; -import https from 'https'; -import { Socket } from 'net'; -import path from 'path'; -import express from 'express'; -import cors from 'cors'; -import fs from 'fs'; -import crypto from 'crypto'; -import glob from 'glob'; -import morgan from 'morgan'; - -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; - -import { initAccounts } from '@Entities/Accounts'; -import { initDomains } from '@Entities/Domains'; -import { initPlaces } from '@Entities/Places'; -import { initTokens } from '@Entities/Tokens'; -import { initSessions } from '@Entities/Sessions'; -import { initRequests } from '@Entities/Requests'; -import { initMonitoring } from '@Monitoring/Monitoring'; - -import { setupDB } from '@Tools/Db'; -import { IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger, initLogging, morganOptions } from '@Tools/Logging'; - -initializeConfiguration() -.catch ( err => { - Logger.error('main: failed configuration: ' + err); - return; -}) -.then( () => { - initLogging(); - initMonitoring(); - initSessions(); - initTokens(); - initRequests(); - initAccounts(); - initDomains(); - initPlaces(); - return setupDB(); -}) -.catch( err => { - Logger.error('main: failure opening database: ' + err); - return; -}) -.then( () => { - // Initialize and start ExpressJS - const expr = express(); - - // Setup the logger of messages - expr.use(morgan('dev', morganOptions)); - - // Set up the CORS allows headers and option handshakes - expr.use(cors({ - 'allowedHeaders': [ 'authorization', 'content-type', 'x-vircadia-error-handle' ], - 'credentials': true - } )); - - // Most of the requests are JSON in an out. - // This parses the JSON and adds 'Request.body' - expr.use(express.json({ - 'strict': false, - 'limit': Config.server['max-body-size'] - })); - - // There is a problem with some of the domain-server requests that don't - // include the final closing curly-bracket on the JSON. Until that - // is fixed, this kludge re-parses the body with the closing curly-bracket - // if there is a JSON parse error. - // In general, a JSON parse error returns HTTP status 400. - expr.use( (err:Error, req:Request, resp:Response, next:NextFunction) => { - if (err instanceof SyntaxError) { - if ('body' in err) { - Logger.error(`JSON parseError: have body and trying reparse with terminator`); - try { - /* tslint:disable-next-line */ - req.body = JSON.parse(err['body'] + '}'); - next(); - } - catch (ex) { - /* tslint:disable-next-line */ - Logger.error(`parseError: ${err['body']}`); - resp.status(400).send({ 'status': 'failure', 'error': 'JSON parse error'}); - }; - } - else { - Logger.error(`JSON parseError: no body`); - resp.status(400).send({ 'status': 'failure', 'error': 'JSON parse error'}); - }; - } - else { - next(); - }; - }); - - // Early router entry to do any early debugging - expr.use(createAPIRouter('routes-first')); - - // The metaverseAPI operations - expr.use(createAPIRouter('routes')); - - // Serving static files - expr.use(Config.server["static-base"] ?? '/static', express.static(path.join(__dirname, 'static'))); - - // If all the other routing didn't work, finally make errors - expr.use(createAPIRouter('routes-last')); - - // Build server to listen for requests - // If certificates are provided, create an https server - let server: http.Server | https.Server; - if (IsNotNullOrEmpty(Config.server["key-file"]) - && IsNotNullOrEmpty(Config.server["cert-file"])) { - try { - const httpsOptions = { - key: fs.readFileSync(Config.server["key-file"], 'utf8'), - cert: fs.readFileSync(Config.server["cert-file"], 'utf8'), - secureProtocol: 'SSLv23_server_method', - /* tslint:disable-next-line */ - secureOptions: crypto.constants.SSL_OP_NO_SSLv3 | crypto.constants.SSL_OP_NO_TLSv1 - }; - server = https.createServer(httpsOptions, expr); - } - catch (err) { - Logger.error(`main: exception initializing https: ${err}`); - }; - } - else { - server = http.createServer(expr); - }; - - // Keep track of connected sockets to enable clean shutdowns - ManageServer(server); - - // When the server is ready, start listening - server.on('listening', () => { - Logger.info(`Started metaverse-server version ${Config.server["server-version"]['version-tag']}`); - }) - .on('error', (err) => { - Logger.error('server exception: ' + err.message); - }) - .listen(Config.server["listen-port"], Config.server["listen-host"]); - - // Receive termination signals, shutdown server, stop receiving requests cleanly - // SIGTERM is usually sent by Docker to stop the application. If this doesn't exit, - // a SIGKILL will be sent in 10 seconds. - process.on('SIGTERM', () => { - Logger.info('SIGTERM'); - ShutdownServer(server, 'SIGTERM'); - }); - // SIGINT (usually cntl-C) means to stop and exit - process.on('SIGINT', () => { - Logger.info('SIGINT'); - ShutdownServer(server, 'SIGINT'); - }); -}) -.catch( err => { - Logger.error('main: bad failure: ' + err); -}); - -// The ExpressJS server won't shutdown if there are open connections -// so this sets up something to remember connections and to destroy -// them when this needs the server to shutdown. -// The ExpressJS operation 'server.close()' will hang until all connections -// close so the connections have to be destroyed to stop the server. -// Technically we could wait if there is a long operation but ShutdownServer() -// is called when the app needs to exit so the caller will -// have to deal with the error. -let nextSockerId: number = 444; -const currentConnections: Map = new Map(); -function ManageServer(pServer: http.Server|https.Server): void { - pServer.on('connection', sock => { - const sockId = nextSockerId++; - currentConnections.set(sockId, sock); - sock.on('close', () => { - currentConnections.delete(sockId); - }); - }) -}; -function ShutdownServer(pServer: http.Server|https.Server, pMsg: string): void { - currentConnections.forEach( (val, id) => { - val.destroy(); - }); - currentConnections.clear(); - pServer.close( () => { - Logger.info(`${pMsg}: Stopped metaverse-server version ${Config.server["server-version"]['version-tag']}`); - process.exit(); - }); -}; - -// Search a directory for .js files that export a 'router' property and return a -// new Router that routes to those exports. -function createAPIRouter(pBaseDir: string): Router { - // Logger.debug('createAPIRouter: adding routes from ' + pBaseDir); - return glob - // find all .js files in the passed subdirectory - .sync('**/*.js', { cwd: `${__dirname}/${pBaseDir}/` }) - // read in those files and create array of all exported objects - .map( filename => require(`./${pBaseDir}/${filename}`)) - // filter down to those things that export a 'router' property - .filter(router => router.hasOwnProperty('router')) - // print out debugging about which routers are being created - .map(rr => { Logger.debug('createAPIRouter: adding ' + rr.name ?? 'UNKNOWN'); return rr; }) - // create a Router and add each found Router and end up with a Router with all found Routers - .reduce((rootRouter, router) => rootRouter.use(router.router), Router({ mergeParams: true } ) ); -}; +// Copyright 2020 Vircadia Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +import "module-alias/register"; + +import { Config, initializeConfiguration } from "@Base/config"; + +import http from "http"; +import https from "https"; +import { Socket } from "net"; +import path from "path"; +import express from "express"; +import cors from "cors"; +import fs from "fs"; +import crypto from "crypto"; +import glob from "glob"; +import morgan from "morgan"; + +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; + +import { initAccounts } from "@Entities/Accounts"; +import { initDomains } from "@Entities/Domains"; +import { initPlaces } from "@Entities/Places"; +import { initTokens } from "@Entities/Tokens"; +import { initSessions } from "@Entities/Sessions"; +import { initRequests } from "@Entities/Requests"; +import { initMonitoring } from "@Monitoring/Monitoring"; + +import { setupDB } from "@Tools/Db"; +import { IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger, initLogging, morganOptions } from "@Tools/Logging"; + +initializeConfiguration() + ["catch"]((err) => { + Logger.error("main: failed configuration: " + err); + + }) + .then(() => { + initLogging(); + initMonitoring(); + initSessions(); + initTokens(); + initRequests(); + initAccounts(); + initDomains(); + initPlaces(); + return setupDB(); + }) + ["catch"]((err) => { + Logger.error("main: failure opening database: " + err); + + }) + .then(() => { + // Initialize and start ExpressJS + const expr = express(); + + // Setup the logger of messages + expr.use(morgan("dev", morganOptions)); + + // Set up the CORS allows headers and option handshakes + expr.use(cors({ + "allowedHeaders": ["authorization", "content-type", "x-vircadia-error-handle"], + "credentials": true + })); + + // Most of the requests are JSON in an out. + // This parses the JSON and adds 'Request.body' + expr.use(express.json({ + "strict": false, + "limit": Config.server["max-body-size"] + })); + + // There is a problem with some of the domain-server requests that don't + // include the final closing curly-bracket on the JSON. Until that + // is fixed, this kludge re-parses the body with the closing curly-bracket + // if there is a JSON parse error. + // In general, a JSON parse error returns HTTP status 400. + expr.use((err: Error, req: Request, resp: Response, next: NextFunction) => { + if (err instanceof SyntaxError) { + if ("body" in err) { + Logger.error(`JSON parseError: have body and trying reparse with terminator`); + try { + // eslint-disable-next-line + req.body = JSON.parse(err['body'] + '}'); + next(); + } catch (ex) { + // eslint-disable-next-line + Logger.error(`parseError: ${err['body']}`); + resp.status(400).send({ "status": "failure", "error": "JSON parse error" }); + } + } else { + Logger.error(`JSON parseError: no body`); + resp.status(400).send({ "status": "failure", "error": "JSON parse error" }); + } + } else { + next(); + } + }); + + // Early router entry to do any early debugging + expr.use(createAPIRouter("routes-first")); + + // The metaverseAPI operations + expr.use(createAPIRouter("routes")); + + // Serving static files + expr.use(Config.server["static-base"] ?? "/static", express["static"](path.join(__dirname, "static"))); + + // If all the other routing didn't work, finally make errors + expr.use(createAPIRouter("routes-last")); + + // Build server to listen for requests + // If certificates are provided, create an https server + let server: http.Server | https.Server; + if (IsNotNullOrEmpty(Config.server["key-file"]) + && IsNotNullOrEmpty(Config.server["cert-file"])) { + try { + const httpsOptions = { + key: fs.readFileSync(Config.server["key-file"], "utf8"), + cert: fs.readFileSync(Config.server["cert-file"], "utf8"), + secureProtocol: "SSLv23_server_method", + // eslint-disable-next-line + secureOptions: crypto.constants.SSL_OP_NO_SSLv3 | crypto.constants.SSL_OP_NO_TLSv1 + }; + server = https.createServer(httpsOptions, expr); + } catch (err) { + Logger.error(`main: exception initializing https: ${err}`); + } + } else { + server = http.createServer(expr); + } + + // Keep track of connected sockets to enable clean shutdowns + ManageServer(server); + + // When the server is ready, start listening + server.on("listening", () => { + Logger.info(`Started metaverse-server version ${Config.server["server-version"]["version-tag"]}`); + }) + .on("error", (err) => { + Logger.error("server exception: " + err.message); + }) + .listen(Config.server["listen-port"], Config.server["listen-host"]); + + // Receive termination signals, shutdown server, stop receiving requests cleanly + // SIGTERM is usually sent by Docker to stop the application. If this doesn't exit, + // a SIGKILL will be sent in 10 seconds. + process.on("SIGTERM", () => { + Logger.info("SIGTERM"); + ShutdownServer(server, "SIGTERM"); + }); + // SIGINT (usually cntl-C) means to stop and exit + process.on("SIGINT", () => { + Logger.info("SIGINT"); + ShutdownServer(server, "SIGINT"); + }); + }) + ["catch"]((err) => { + Logger.error("main: bad failure: " + err); + }); + +// The ExpressJS server won't shutdown if there are open connections +// so this sets up something to remember connections and to destroy +// them when this needs the server to shutdown. +// The ExpressJS operation 'server.close()' will hang until all connections +// close so the connections have to be destroyed to stop the server. +// Technically we could wait if there is a long operation but ShutdownServer() +// is called when the app needs to exit so the caller will +// have to deal with the error. +let nextSockerId = 444; +const currentConnections: Map = new Map(); +function ManageServer(pServer: http.Server | https.Server): void { + pServer.on("connection", (sock) => { + const sockId = nextSockerId++; + currentConnections.set(sockId, sock); + sock.on("close", () => { + currentConnections["delete"](sockId); + }); + }); +} +function ShutdownServer(pServer: http.Server | https.Server, pMsg: string): void { + currentConnections.forEach((val, id) => { + val.destroy(); + }); + currentConnections.clear(); + pServer.close(() => { + Logger.info(`${pMsg}: Stopped metaverse-server version ${Config.server["server-version"]["version-tag"]}`); + process.exit(); + }); +} + +// Search a directory for .js files that export a 'router' property and return a +// new Router that routes to those exports. +function createAPIRouter(pBaseDir: string): Router { + // Logger.debug('createAPIRouter: adding routes from ' + pBaseDir); + return glob + // find all .js files in the passed subdirectory + .sync("**/*.js", { cwd: `${__dirname}/${pBaseDir}/` }) + // read in those files and create array of all exported objects + .map((filename) => { + return require(`./${pBaseDir}/${filename}`); + }) + // filter down to those things that export a 'router' property + .filter((router) => { + return router.hasOwnProperty("router"); + }) + // print out debugging about which routers are being created + .map((rr) => { + Logger.debug("createAPIRouter: adding " + rr.name ?? "UNKNOWN"); return rr; + }) + // create a Router and add each found Router and end up with a Router with all found Routers + .reduce((rootRouter, router) => { + return rootRouter.use(router.router); + }, Router({ mergeParams: true })); +} diff --git a/src/route-tools/EntityFieldDefn.ts b/src/route-tools/EntityFieldDefn.ts index dea3b274..d91dae02 100644 --- a/src/route-tools/EntityFieldDefn.ts +++ b/src/route-tools/EntityFieldDefn.ts @@ -12,20 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Entity } from '@Entities/Entity'; -import { AuthToken } from '@Entities/AuthToken'; +import { Entity } from "@Entities/Entity"; +import { AuthToken } from "@Entities/AuthToken"; -import { VKeyedCollection } from '@Tools/vTypes'; +import { VKeyedCollection } from "@Tools/vTypes"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; export interface ValidateResponse { - valid: boolean, - reason?: string -}; + valid: boolean, + reason?: string +} // A start at having a table driven access permissions to the Entity attributes. // Each Entity field would have an entry that includes who can read or write @@ -47,5 +46,5 @@ export interface FieldDefn { getter: getterFunction, setter: setterFunction, updater?: updaterFunction -}; +} export type EntityFieldsDefn = { [ key: string]: FieldDefn }; diff --git a/src/route-tools/GetterSetter.ts b/src/route-tools/GetterSetter.ts index 989330db..cf1170ce 100755 --- a/src/route-tools/GetterSetter.ts +++ b/src/route-tools/GetterSetter.ts @@ -12,128 +12,119 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Entity } from '@Entities/Entity'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { AuthToken } from '@Entities/AuthToken'; +import { Entity } from "@Entities/Entity"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { AuthToken } from "@Entities/AuthToken"; -import { EntityFieldsDefn, FieldDefn, ValidateResponse } from '@Route-Tools/EntityFieldDefn'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; +import { EntityFieldsDefn, FieldDefn, ValidateResponse } from "@Route-Tools/EntityFieldDefn"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; // Get the value of a entity field with the fieldname. // Checks to make sure the getter has permission to get the values. // Returns the value. Could be 'undefined' whether the requestor doesn't have permissions or that's // the actual field value. export async function getEntityField( - pPerms: EntityFieldsDefn, - pAuthToken: AuthToken, pEntity: Entity, - pField: string, pRequestingAccount?: AccountEntity): Promise { - let val; - const perms = pPerms[pField]; - if (perms) { - Logger.cdebug('field-setting', `getEntityField: get ${pField}, perms.entity_field=${perms.entity_field}`); - if (await checkAccessToEntity(pAuthToken, pEntity, perms.get_permissions, pRequestingAccount)) { - Logger.cdebug('field-setting', `getEntityField: access passed`); - if (typeof(perms.getter) === 'function') { - Logger.cdebug('field-setting', `getEntityField: doing getter`); - val = await perms.getter(perms, pEntity); - Logger.cdebug('field-setting', `getEntityField: ${pField}=>${JSON.stringify(val)}`); - } - else { - Logger.cdebug('field-setting', `getEntityField: no function getter ${pField}, perms.entity_field=${perms.entity_field}, typeof(perms.getter)=${typeof(perms.getter)}`); - Logger.cdebug('field-setting', `getEntityField: no function getter perms=${JSON.stringify(perms)}`); - }; - }; - }; - return val; -}; + pPerms: EntityFieldsDefn, + pAuthToken: AuthToken, pEntity: Entity, + pField: string, pRequestingAccount?: AccountEntity): Promise { + let val; + const perms = pPerms[pField]; + if (perms) { + Logger.cdebug("field-setting", `getEntityField: get ${pField}, perms.entity_field=${perms.entity_field}`); + if (await checkAccessToEntity(pAuthToken, pEntity, perms.get_permissions, pRequestingAccount)) { + Logger.cdebug("field-setting", `getEntityField: access passed`); + if (typeof perms.getter === "function") { + Logger.cdebug("field-setting", `getEntityField: doing getter`); + val = await perms.getter(perms, pEntity); + Logger.cdebug("field-setting", `getEntityField: ${pField}=>${JSON.stringify(val)}`); + } else { + Logger.cdebug("field-setting", `getEntityField: no function getter ${pField}, perms.entity_field=${perms.entity_field}, typeof(perms.getter)=${typeof perms.getter}`); + Logger.cdebug("field-setting", `getEntityField: no function getter perms=${JSON.stringify(perms)}`); + } + } + } + return val; +} // Set a entity field with the fieldname and a value. // Checks to make sure the setter has permission to set. // Returns a ValidateResponse that can include a field giving the reason for the error export async function setEntityField( - pPerms: EntityFieldsDefn, - pAuthToken: AuthToken, // authorization for making this change - pEntity: Entity, // the entity being changed - pField: string, pVal: any, // field being changed and the new value - pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known - pUpdates?: VKeyedCollection // where to record updates made (optional) - ): Promise { - let validity: ValidateResponse; - const perms = pPerms[pField]; - if (perms) { - try { - Logger.cdebug('field-setting', `setEntityField: ${pField}=>${JSON.stringify(pVal)}`); - if (await checkAccessToEntity(pAuthToken, pEntity, perms.set_permissions, pRequestingAccount)) { - Logger.cdebug('field-setting', `setEntityField: access passed`); - validity = await perms.validate(perms, pEntity, pVal, pAuthToken); - if (validity.valid) { - Logger.cdebug('field-setting', `setEntityField: value validated`); - if (typeof(perms.setter) === 'function') { - await perms.setter(perms, pEntity, pVal); - if (pUpdates) { - getEntityUpdateForField(pPerms, pEntity, pField, pUpdates); - }; - }; + pPerms: EntityFieldsDefn, + pAuthToken: AuthToken, // authorization for making this change + pEntity: Entity, // the entity being changed + pField: string, pVal: any, // field being changed and the new value + pRequestingAccount?: AccountEntity, // Account associated with pAuthToken, if known + pUpdates?: VKeyedCollection // where to record updates made (optional) +): Promise { + let validity: ValidateResponse; + const perms = pPerms[pField]; + if (perms) { + try { + Logger.cdebug("field-setting", `setEntityField: ${pField}=>${JSON.stringify(pVal)}`); + if (await checkAccessToEntity(pAuthToken, pEntity, perms.set_permissions, pRequestingAccount)) { + Logger.cdebug("field-setting", `setEntityField: access passed`); + validity = await perms.validate(perms, pEntity, pVal, pAuthToken); + if (validity.valid) { + Logger.cdebug("field-setting", `setEntityField: value validated`); + if (typeof perms.setter === "function") { + await perms.setter(perms, pEntity, pVal); + if (pUpdates) { + getEntityUpdateForField(pPerms, pEntity, pField, pUpdates); + } + } + } else { + Logger.cdebug("field-setting", `value did not validate`); + } + } else { + validity = { valid: false, reason: "account cannot set field" }; + } + } catch (err) { + Logger.error(`setEntityField: exception: ${err}`); + validity = { valid: false, reason: "exception setting: " + err }; } - else { - Logger.cdebug('field-setting', `value did not validate`); - }; - } - else { - validity = { valid: false, reason: 'account cannot set field' }; - }; + } else { + Logger.error(`setEntityField: no perms field. Field=${pField}`); + validity = { valid: false, reason: `setEntityField: no perms field. Field=${pField}` }; } - catch (err) { - Logger.error(`setEntityField: exception: ${err}`); - validity = { valid: false, reason: 'exception setting: ' + err }; - }; - } - else { - Logger.error(`setEntityField: no perms field. Field=${pField}`); - validity = { valid: false, reason: `setEntityField: no perms field. Field=${pField}`}; - }; - return validity; -}; + return validity; +} // Generate an 'update' block for the specified field or fields. // This is a field/value collection that can be passed to the database routines. // Note that this directly fetches the field value rather than using 'getter' since // we want the actual value (whatever it is) to go into the database. // If an existing VKeyedCollection is passed, it is added to an returned. export function getEntityUpdateForField( - pPerms: EntityFieldsDefn, - pEntity: Entity, - pField: string | string[], - pExisting?: VKeyedCollection): VKeyedCollection { - const ret: VKeyedCollection = pExisting ?? {}; - if (Array.isArray(pField)) { - pField.forEach( fld => { - const perms = pPerms[fld]; - makeEntityFieldUpdate(perms, pEntity, ret); - }); - } - else { - const perms = pPerms[pField]; - makeEntityFieldUpdate(perms, pEntity, ret); - }; - return ret; -}; + pPerms: EntityFieldsDefn, + pEntity: Entity, + pField: string | string[], + pExisting?: VKeyedCollection): VKeyedCollection { + const ret: VKeyedCollection = pExisting ?? {}; + if (Array.isArray(pField)) { + pField.forEach((fld) => { + const perms = pPerms[fld]; + makeEntityFieldUpdate(perms, pEntity, ret); + }); + } else { + const perms = pPerms[pField]; + makeEntityFieldUpdate(perms, pEntity, ret); + } + return ret; +} // if the field has an updater, do that, else just create an update for the base named field. // This modifies the passed VKeyedCollection with the field and new value to pass to // the database. function makeEntityFieldUpdate(pPerms: FieldDefn, pEntity: Entity, pRet: VKeyedCollection): void { - if (pPerms) { - if (pPerms.updater) { - pPerms.updater(pPerms, pEntity, pRet); + if (pPerms) { + if (pPerms.updater) { + pPerms.updater(pPerms, pEntity, pRet); + } else { + pRet[pPerms.entity_field] = (pEntity as any)[pPerms.entity_field]; + } } - else { - pRet[pPerms.entity_field] = (pEntity as any)[pPerms.entity_field]; - }; - }; -}; - +} diff --git a/src/route-tools/Perm.ts b/src/route-tools/Perm.ts index e8dca732..d259a80e 100644 --- a/src/route-tools/Perm.ts +++ b/src/route-tools/Perm.ts @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; // Permission codes: // 'all': any one @@ -24,16 +23,15 @@ // 'sponsor': the requesting account is the sponsor of the traget domain // 'domainaccess': the target entity has a domain and requesting account must be sponsor export class Perm { - public static NONE = 'none'; - public static ALL = 'all'; - public static PUBLIC = 'public'; // target account is publicly visible - public static DOMAIN = 'domain'; // check against .sponsorId - public static OWNER = 'owner'; // check against .id or .accountId - public static FRIEND = 'friend'; // check member of .friends - public static CONNECTION = 'connection';// check member of .connections - public static ADMIN = 'admin'; // check if isAdmin - public static SPONSOR = 'sponsor'; // check against .sponsorAccountId - public static MANAGER = 'manager'; // check against .managers - public static DOMAINACCESS = 'domainaccess'; // check that entity's domain has access -}; - + public static NONE = "none"; + public static ALL = "all"; + public static PUBLIC = "public"; // target account is publicly visible + public static DOMAIN = "domain"; // check against .sponsorId + public static OWNER = "owner"; // check against .id or .accountId + public static FRIEND = "friend"; // check member of .friends + public static CONNECTION = "connection";// check member of .connections + public static ADMIN = "admin"; // check if isAdmin + public static SPONSOR = "sponsor"; // check against .sponsorAccountId + public static MANAGER = "manager"; // check against .managers + public static DOMAINACCESS = "domainaccess"; // check that entity's domain has access +} diff --git a/src/route-tools/Permissions.ts b/src/route-tools/Permissions.ts index 4b9d8665..7a09d9cd 100755 --- a/src/route-tools/Permissions.ts +++ b/src/route-tools/Permissions.ts @@ -12,22 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Entity } from '@Entities/Entity'; -import { Accounts } from '@Entities/Accounts'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { Domains } from '@Entities/Domains'; -import { DomainEntity } from '@Entities/DomainEntity'; -import { AuthToken } from '@Entities/AuthToken'; -import { Tokens, TokenScope } from '@Entities/Tokens'; +import { Entity } from "@Entities/Entity"; +import { Accounts } from "@Entities/Accounts"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { Domains } from "@Entities/Domains"; +import { DomainEntity } from "@Entities/DomainEntity"; +import { AuthToken } from "@Entities/AuthToken"; +import { Tokens, TokenScope } from "@Entities/Tokens"; -import { Perm } from '@Route-Tools/Perm'; +import { Perm } from "@Route-Tools/Perm"; -import { SArray, VKeyedCollection } from '@Tools/vTypes'; -import { IsNotNullOrEmpty, IsNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; -import { Availability } from '@Entities/Sets/Availability'; +import { SArray, VKeyedCollection } from "@Tools/vTypes"; +import { IsNotNullOrEmpty, IsNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; +import { Availability } from "@Entities/Sets/Availability"; // Check if the passed AuthToken has access to the passed Entity. // Generalized for any Entity. The permissions expect 'accountId' and 'sponsorAccountId' @@ -40,17 +39,17 @@ import { Availability } from '@Entities/Sets/Availability'; // Note that the list of RequiredAccess is a OR list -- any one access type is sufficient. // Note that pAuthToken can be passed as 'undefined'. export async function checkAccessToEntity(pAuthToken: AuthToken, // token being used to access - pTargetEntity: Entity, // entity being accessed - pRequiredAccess: string[], // permissions required to access domain - pRequestingAccount?: AccountEntity // requesting account if known - ): Promise { + pTargetEntity: Entity, // entity being accessed + pRequiredAccess: string[], // permissions required to access domain + pRequestingAccount?: AccountEntity // requesting account if known +): Promise { let requestingAccount = pRequestingAccount; - let canAccess: boolean = false; + let canAccess = false; if (IsNotNullOrEmpty(pTargetEntity)) { for (const perm of pRequiredAccess) { - Logger.cdebug('field-setting', `checkAccessToEntity: checking ${perm}`); + Logger.cdebug("field-setting", `checkAccessToEntity: checking ${perm}`); switch (perm) { case Perm.ALL: canAccess = true; @@ -58,111 +57,111 @@ export async function checkAccessToEntity(pAuthToken: AuthToken, // token being case Perm.PUBLIC: // The target entity is publicly visible // Mostly AccountEntities that must have an 'availability' field - if (pTargetEntity.hasOwnProperty('availability')) { + if (pTargetEntity.hasOwnProperty("availability")) { if ((pTargetEntity as AccountEntity).availability.includes(Availability.ALL)) { canAccess = true; - }; - }; + } + } break; case Perm.DOMAIN: // requestor is a domain and it's account is the domain's sponsoring account if (pAuthToken && SArray.has(pAuthToken.scope, TokenScope.DOMAIN)) { - if (pTargetEntity.hasOwnProperty('sponsorAccountId')) { - Logger.cdebug('field-setting', `checkAccessToEntity: authToken is domain. auth.AccountId=${pAuthToken.accountId}, sponsor=${(pTargetEntity as any).sponsorAccountId}`); + if (pTargetEntity.hasOwnProperty("sponsorAccountId")) { + Logger.cdebug("field-setting", `checkAccessToEntity: authToken is domain. auth.AccountId=${pAuthToken.accountId}, sponsor=${(pTargetEntity as any).sponsorAccountId}`); canAccess = pAuthToken.accountId === (pTargetEntity as any).sponsorAccountId; - } - else { + } else { // Super special case where domain doesn't have a sponsor but has an api_key. // In this case, the API_KEY is put in the accountId field of the DOMAIN scoped AuthToken - if (pTargetEntity.hasOwnProperty('apiKey')) { + if (pTargetEntity.hasOwnProperty("apiKey")) { canAccess = pAuthToken.accountId === (pTargetEntity as any).apiKey; - }; - }; - }; + } + } + } break; case Perm.OWNER: // The requestor wants to be the same account as the target entity - if (pAuthToken && pTargetEntity.hasOwnProperty('id')) { + if (pAuthToken && pTargetEntity.hasOwnProperty("id")) { canAccess = pAuthToken.accountId === (pTargetEntity as AccountEntity).id; - }; - if (!canAccess && pTargetEntity.hasOwnProperty('accountId')) { + } + if (!canAccess && pTargetEntity.hasOwnProperty("accountId")) { canAccess = pAuthToken.accountId === (pTargetEntity as any).accountId; - }; + } break; case Perm.FRIEND: // The requestor is a 'friend' of the target entity - if (pAuthToken && pTargetEntity.hasOwnProperty('friends')) { + if (pAuthToken && pTargetEntity.hasOwnProperty("friends")) { const targetFriends: string[] = (pTargetEntity as AccountEntity).friends; if (targetFriends) { requestingAccount = requestingAccount ?? await Accounts.getAccountWithId(pAuthToken.accountId); canAccess = SArray.hasNoCase(targetFriends, requestingAccount.username); - }; - }; + } + } break; case Perm.CONNECTION: // The requestor is a 'connection' of the target entity - if (pAuthToken && pTargetEntity.hasOwnProperty('connections')) { + if (pAuthToken && pTargetEntity.hasOwnProperty("connections")) { const targetConnections: string[] = (pTargetEntity as AccountEntity).connections; if (targetConnections) { requestingAccount = requestingAccount ?? await Accounts.getAccountWithId(pAuthToken.accountId); canAccess = SArray.hasNoCase(targetConnections, requestingAccount.username); - }; - }; + } + } break; case Perm.ADMIN: if (pAuthToken && Tokens.isSpecialAdminToken(pAuthToken)) { - Logger.cdebug('field-setting', `checkAccessToEntity: isSpecialAdminToken`); + Logger.cdebug("field-setting", `checkAccessToEntity: isSpecialAdminToken`); canAccess = true; - } - else { + } else { // If the authToken is an account, has access if admin if (pAuthToken && SArray.has(pAuthToken.scope, TokenScope.OWNER)) { - Logger.cdebug('field-setting', `checkAccessToEntity: admin. auth.AccountId=${pAuthToken.accountId}`); + Logger.cdebug("field-setting", `checkAccessToEntity: admin. auth.AccountId=${pAuthToken.accountId}`); requestingAccount = requestingAccount ?? await Accounts.getAccountWithId(pAuthToken.accountId); canAccess = Accounts.isAdmin(requestingAccount); - }; - }; + } + } break; case Perm.SPONSOR: // Requestor is a regular account and is the sponsor of the domain if (pAuthToken && SArray.has(pAuthToken.scope, TokenScope.OWNER)) { - if (pTargetEntity.hasOwnProperty('sponsorAccountId')) { - Logger.cdebug('field-setting', `checkAccessToEntity: authToken is domain. auth.AccountId=${pAuthToken.accountId}, sponsor=${(pTargetEntity as any).sponsorAccountId}`); + if (pTargetEntity.hasOwnProperty("sponsorAccountId")) { + Logger.cdebug("field-setting", `checkAccessToEntity: authToken is domain. auth.AccountId=${pAuthToken.accountId}, sponsor=${(pTargetEntity as any).sponsorAccountId}`); canAccess = pAuthToken.accountId === (pTargetEntity as DomainEntity).sponsorAccountId; - }; - }; + } + } break; case Perm.MANAGER: // See if requesting account is in the list of managers of this entity if (pAuthToken && SArray.has(pAuthToken.scope, TokenScope.OWNER)) { - if (pTargetEntity.hasOwnProperty('managers')) { + if (pTargetEntity.hasOwnProperty("managers")) { requestingAccount = requestingAccount ?? await Accounts.getAccountWithId(pAuthToken.accountId); if (requestingAccount) { const managers: string[] = (pTargetEntity as DomainEntity).managers; // Logger.debug(`Perm.MANAGER: managers=${JSON.stringify(managers)}, target=${requestingAccount.username}`); if (managers && managers.includes(requestingAccount.username.toLowerCase())) { canAccess = true; - }; - }; - }; - }; + } + } + } + } break; case Perm.DOMAINACCESS: // Target entity has a domain reference and verify the requestor is able to reference that domain - if (pAuthToken && pTargetEntity.hasOwnProperty('domainId')) { + if (pAuthToken && pTargetEntity.hasOwnProperty("domainId")) { const aDomain = await Domains.getDomainWithId((pTargetEntity as any).domainId); if (aDomain) { canAccess = aDomain.sponsorAccountId === pAuthToken.accountId; - }; - }; + } + } break; default: canAccess = false; break; - }; + } // If some permission allows access, we are done - if (canAccess) break; - }; - }; + if (canAccess) { + break; + } + } + } return canAccess; -}; \ No newline at end of file +} diff --git a/src/route-tools/RESTResponse.ts b/src/route-tools/RESTResponse.ts index 03bf5a43..6e035ec2 100755 --- a/src/route-tools/RESTResponse.ts +++ b/src/route-tools/RESTResponse.ts @@ -12,126 +12,123 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import Config from '../config'; +import Config from "../config"; -import { Request, Response } from 'express'; +import { Request, Response } from "express"; -import { Monitoring } from '@Monitoring/Monitoring'; +import { Monitoring } from "@Monitoring/Monitoring"; -import { IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; export enum HTTPStatusCode { - OK = 200, - Found = 302, - BadRequest = 400, - Unauthorized = 401, - Forbidden = 403, - NotFound = 404, -}; + OK = 200, + Found = 302, + BadRequest = 400, + Unauthorized = 401, + Forbidden = 403, + NotFound = 404 +} export class RESTResponse { - IsFailure: boolean; - Status: string; - HTTPStatus: number; - Data: any; - - private _request: Request; - private _response: Response; - private _additionalFields: Map = new Map(); - - constructor(pReq : Request, pResp: Response) { - this._request = pReq; - this._response = pResp; - this.IsFailure = false; - this.Status = 'success'; - this.HTTPStatus = HTTPStatusCode.OK; - }; - - getRequest(): Request { - return this._request; - }; - - getResponse(): Response { - return this._response; - }; - - private _authToken:string = null; - getAuthToken(): string { - if (this._authToken === null) { - const auth = this._request.headers.authorization; - if (auth) { - if (auth.toLowerCase().startsWith('bearer ')) { - this._authToken = auth.substr(7); + IsFailure: boolean; + Status: string; + HTTPStatus: number; + Data: any; + + private _request: Request; + private _response: Response; + private _additionalFields: Map = new Map(); + + constructor(pReq: Request, pResp: Response) { + this._request = pReq; + this._response = pResp; + this.IsFailure = false; + this.Status = "success"; + this.HTTPStatus = HTTPStatusCode.OK; + } + + getRequest(): Request { + return this._request; + } + + getResponse(): Response { + return this._response; + } + + private _authToken: string = null; + getAuthToken(): string { + if (this._authToken === null) { + const auth = this._request.headers.authorization; + if (auth) { + if (auth.toLowerCase().startsWith("bearer ")) { + this._authToken = auth.substr(7); + } else { + this._authToken = auth; + } + } } - else { - this._authToken = auth; + return this._authToken; + } + + respondSuccess(): RESTResponse { + this.Status = "success"; + return this; + } + + respondFailure(msg1: string, msg2?: string): RESTResponse { + this.Status = "failure"; + this.IsFailure = true; + this._additionalFields.set("error", msg1); + if (msg2) { + this._additionalFields.set("errorInfo", msg2); } - } + return this; } - return this._authToken; - } - - respondSuccess() : RESTResponse { - this.Status = 'success'; - return this; - }; - - respondFailure( msg1: string, msg2?: string ) : RESTResponse { - this.Status = 'failure'; - this.IsFailure = true; - this._additionalFields.set('error', msg1); - if (msg2) { - this._additionalFields.set('errorInfo', msg2); + + addAdditionalField(pFieldName: string, pFieldValue: any) { + this._additionalFields.set(pFieldName, pFieldValue); } - return this; - }; - - addAdditionalField( pFieldName: string, pFieldValue: any) { - this._additionalFields.set(pFieldName, pFieldValue); - }; - - // Build JSON response into the http response structure - // If successful, returns the standard response of - // { 'status': 'success', 'data': this._data } - // Extra top-level things are added if in _addtionalFields - buildRESTResponse() : any { - let responseData: any; - if (this.IsFailure) { - // If a specific header is in the request, return errors as - // HTTP badrequest errors rather than just returning the JSON status - if (Config["metaverse-server"]["http-error-on-failure"]) { - const errorHeader = Config["metaverse-server"]["error-header"]; - const errorAction = this._request.get(errorHeader); - // Logger.cdebug('metaverseapi-response-detail', `buildRESTResponse: failing. errorAction=${errorAction}`); - if (errorAction && errorAction.toLowerCase() === "badrequest") { - this._response.statusCode = HTTPStatusCode.BadRequest; - }; - }; - // Set status to 'failure'. Any additional error info is added by _additionalFields. - // Logger.cdebug('metaverseapi-response-detail', `buildRESTResponse: failing to ${this._response.statusCode}`); - responseData = { - 'status': 'failure' - }; - Monitoring.event('apiErrors', 1); + + // Build JSON response into the http response structure + // If successful, returns the standard response of + // { 'status': 'success', 'data': this._data } + // Extra top-level things are added if in _addtionalFields + buildRESTResponse(): any { + let responseData: any; + if (this.IsFailure) { + // If a specific header is in the request, return errors as + // HTTP badrequest errors rather than just returning the JSON status + if (Config["metaverse-server"]["http-error-on-failure"]) { + const errorHeader = Config["metaverse-server"]["error-header"]; + const errorAction = this._request.get(errorHeader); + // Logger.cdebug('metaverseapi-response-detail', `buildRESTResponse: failing. errorAction=${errorAction}`); + if (errorAction && errorAction.toLowerCase() === "badrequest") { + this._response.statusCode = HTTPStatusCode.BadRequest; + } + } + // Set status to 'failure'. Any additional error info is added by _additionalFields. + // Logger.cdebug('metaverseapi-response-detail', `buildRESTResponse: failing to ${this._response.statusCode}`); + responseData = { + "status": "failure" + }; + Monitoring.event("apiErrors", 1); + } else { + responseData = { + "status": this.Status + }; + } + + if (IsNotNullOrEmpty(this.Data)) { + responseData.data = this.Data; + } + + if (this._additionalFields.size > 0) { + this._additionalFields.forEach((val, key) => { + responseData[key] = val; + }); + } + return responseData; } - else { - responseData = { - 'status': this.Status, - }; - }; - - if (IsNotNullOrEmpty(this.Data)) { - responseData.data = this.Data; - }; - - if (this._additionalFields.size > 0) { - this._additionalFields.forEach( (val, key) => { - responseData[key] = val; - }); - }; - return responseData; - }; -}; +} diff --git a/src/route-tools/Util.ts b/src/route-tools/Util.ts index 24027536..4548d83c 100755 --- a/src/route-tools/Util.ts +++ b/src/route-tools/Util.ts @@ -12,43 +12,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Request } from 'express'; +import { Request } from "express"; -import { Accounts } from '@Entities/Accounts'; -import { AccountEntity } from '@Entities/AccountEntity'; +import { Accounts } from "@Entities/Accounts"; +import { AccountEntity } from "@Entities/AccountEntity"; -import { Domains } from '@Entities/Domains'; -import { DomainEntity } from '@Entities/DomainEntity'; +import { Domains } from "@Entities/Domains"; +import { DomainEntity } from "@Entities/DomainEntity"; -import { Places } from '@Entities/Places'; -import { PlaceEntity } from '@Entities/PlaceEntity'; +import { Places } from "@Entities/Places"; +import { PlaceEntity } from "@Entities/PlaceEntity"; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; -import { createPublicKey } from 'crypto'; -import { VKeyedCollection, VKeyValue } from '@Tools/vTypes'; -import { IsNotNullOrEmpty, IsNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { createPublicKey } from "crypto"; +import { VKeyedCollection, VKeyValue } from "@Tools/vTypes"; +import { IsNotNullOrEmpty, IsNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; -import { Maturity } from '@Entities/Sets/Maturity'; -import { Visibility } from '@Entities/Sets/Visibility'; +import { Maturity } from "@Entities/Sets/Maturity"; +import { Visibility } from "@Entities/Sets/Visibility"; // The public_key is sent as a binary (DER) form of a PKCS1 key. // To keep backward compatibility, we convert the PKCS1 key into a SPKI key in PEM format // ("PEM" format is "Privacy Enhanced Mail" format and has the "BEGIN" and "END" text included). export function convertBinKeyToPEM(pBinKey: Buffer): string { // Convert the passed binary into a crypto.KeyObject - const publicKey = createPublicKey( { + const publicKey = createPublicKey({ key: pBinKey, - format: 'der', - type: 'pkcs1' + format: "der", + type: "pkcs1" }); // Convert the public key to 'SubjectPublicKeyInfo' (SPKI) format as a PEM string - const convertedKey = publicKey.export({ type: 'spki', format: 'pem' }); + const convertedKey = publicKey["export"]({ type: "spki", format: "pem" }); return convertedKey as string; -}; +} // The legacy interface returns public keys as a stripped PEM key. // "stripped" in that the bounding "BEGIN" and "END" lines have been removed. @@ -56,15 +55,15 @@ export function convertBinKeyToPEM(pBinKey: Buffer): string { export function createSimplifiedPublicKey(pPubKey: string): string { let keyLines: string[] = []; if (pPubKey) { - keyLines = pPubKey.split('\n'); + keyLines = pPubKey.split("\n"); keyLines.shift(); // Remove the "BEGIN" first line while (keyLines.length > 1 - && ( keyLines[keyLines.length-1].length < 1 || keyLines[keyLines.length-1].includes('END PUBLIC KEY') ) ) { + && (keyLines[keyLines.length - 1].length < 1 || keyLines[keyLines.length - 1].includes("END PUBLIC KEY"))) { keyLines.pop(); // Remove the "END" last line - }; + } } - return keyLines.join(''); // Combine all lines into one long string -}; + return keyLines.join(""); // Combine all lines into one long string +} // Process the user request data and update fields fields in the account. // This is infomation from the user from heartbeats or location updates. @@ -75,21 +74,27 @@ export async function updateLocationInfo(pReq: Request): Promise { const aDomain = await Domains.getDomainWithId(pAcct.locationDomainId); if (IsNotNullOrEmpty(aDomain)) { ret = { - 'root': { - 'domain': await buildDomainInfo(aDomain), + "root": { + "domain": await buildDomainInfo(aDomain) }, - 'path': pAcct.locationPath, + "path": pAcct.locationPath }; - } - else { + } else { // The domain doesn't have an ID ret = { - 'root': { - 'domain': { - 'network_address': pAcct.locationNetworkAddress, - 'network_port': pAcct.locationNetworkPort + "root": { + "domain": { + "network_address": pAcct.locationNetworkAddress, + "network_port": pAcct.locationNetworkPort } } }; - }; - }; + } + } ret.node_id = pAcct.locationNodeId; - ret.online = Accounts.isOnline(pAcct) + ret.online = Accounts.isOnline(pAcct); return ret; -}; +} // A smaller, top-level domain info block export async function buildDomainInfo(pDomain: DomainEntity): Promise { return { - 'id': pDomain.id, - 'domainId': pDomain.id, - 'name': pDomain.name, - 'visibility': pDomain.visibility ?? Visibility.OPEN, - 'capacity': pDomain.capacity, - 'sponsorAccountId': pDomain.sponsorAccountId, - 'label': pDomain.name, - 'network_address': pDomain.networkAddr, - 'network_port': pDomain.networkPort, - 'ice_server_address': pDomain.iceServerAddr, - 'version': pDomain.version, - 'protocol_version': pDomain.protocol, - 'active': pDomain.active ?? false, - 'time_of_last_heartbeat': pDomain.timeOfLastHeartbeat?.toISOString(), - 'time_of_last_heartbeat_s': pDomain.timeOfLastHeartbeat?.getTime().toString(), - 'num_users': pDomain.numUsers + "id": pDomain.id, + "domainId": pDomain.id, + "name": pDomain.name, + "visibility": pDomain.visibility ?? Visibility.OPEN, + "capacity": pDomain.capacity, + "sponsorAccountId": pDomain.sponsorAccountId, + "label": pDomain.name, + "network_address": pDomain.networkAddr, + "network_port": pDomain.networkPort, + "ice_server_address": pDomain.iceServerAddr, + "version": pDomain.version, + "protocol_version": pDomain.protocol, + "active": pDomain.active ?? false, + "time_of_last_heartbeat": pDomain.timeOfLastHeartbeat?.toISOString(), + "time_of_last_heartbeat_s": pDomain.timeOfLastHeartbeat?.getTime().toString(), + "num_users": pDomain.numUsers }; -}; +} // Return a structure with the usual domain information. export async function buildDomainInfoV1(pDomain: DomainEntity): Promise { return { - 'domainId': pDomain.id, - 'id': pDomain.id, // legacy - 'name': pDomain.name, - 'visibility': pDomain.visibility ?? Visibility.OPEN, - 'world_name': pDomain.name, // legacy - 'label': pDomain.name, // legacy - 'public_key': pDomain.publicKey ? createSimplifiedPublicKey(pDomain.publicKey) : undefined, - 'owner_places': await buildPlacesForDomain(pDomain), - 'sponsor_account_id': pDomain.sponsorAccountId, - 'ice_server_address': pDomain.iceServerAddr, - 'version': pDomain.version, - 'protocol_version': pDomain.protocol, - 'network_address': pDomain.networkAddr, - 'network_port': pDomain.networkPort, - 'automatic_networking': pDomain.networkingMode, - 'restricted': pDomain.restricted, - 'num_users': pDomain.numUsers, - 'anon_users': pDomain.anonUsers, - 'total_users': pDomain.numUsers, - 'capacity': pDomain.capacity, - 'description': pDomain.description, - 'maturity': pDomain.maturity ?? Maturity.UNRATED, - 'restriction': pDomain.restriction, - 'managers': pDomain.managers, - 'tags': pDomain.tags, - 'meta': { - 'capacity': pDomain.capacity, - 'contact_info': pDomain.contactInfo, - 'description': pDomain.description, - 'images': pDomain.images, - 'managers': pDomain.managers, - 'restriction': pDomain.restriction, - 'tags': pDomain.tags, - 'thumbnail': pDomain.thumbnail, - 'world_name': pDomain.name + "domainId": pDomain.id, + "id": pDomain.id, // legacy + "name": pDomain.name, + "visibility": pDomain.visibility ?? Visibility.OPEN, + "world_name": pDomain.name, // legacy + "label": pDomain.name, // legacy + "public_key": pDomain.publicKey ? createSimplifiedPublicKey(pDomain.publicKey) : undefined, + "owner_places": await buildPlacesForDomain(pDomain), + "sponsor_account_id": pDomain.sponsorAccountId, + "ice_server_address": pDomain.iceServerAddr, + "version": pDomain.version, + "protocol_version": pDomain.protocol, + "network_address": pDomain.networkAddr, + "network_port": pDomain.networkPort, + "automatic_networking": pDomain.networkingMode, + "restricted": pDomain.restricted, + "num_users": pDomain.numUsers, + "anon_users": pDomain.anonUsers, + "total_users": pDomain.numUsers, + "capacity": pDomain.capacity, + "description": pDomain.description, + "maturity": pDomain.maturity ?? Maturity.UNRATED, + "restriction": pDomain.restriction, + "managers": pDomain.managers, + "tags": pDomain.tags, + "meta": { + "capacity": pDomain.capacity, + "contact_info": pDomain.contactInfo, + "description": pDomain.description, + "images": pDomain.images, + "managers": pDomain.managers, + "restriction": pDomain.restriction, + "tags": pDomain.tags, + "thumbnail": pDomain.thumbnail, + "world_name": pDomain.name }, - 'users': { - 'num_anon_users': pDomain.anonUsers, - 'num_users': pDomain.numUsers, - 'user_hostnames': pDomain.hostnames + "users": { + "num_anon_users": pDomain.anonUsers, + "num_users": pDomain.numUsers, + "user_hostnames": pDomain.hostnames }, - 'time_of_last_heartbeat': pDomain.timeOfLastHeartbeat?.toISOString(), - 'time_of_last_heartbeat_s': pDomain.timeOfLastHeartbeat?.getTime().toString(), - 'last_sender_key': pDomain.lastSenderKey, - 'addr_of_first_contact': pDomain.iPAddrOfFirstContact, - 'when_domain_entry_created': pDomain.whenCreated?.toISOString(), - 'when_domain_entry_created_s': pDomain.whenCreated?.getTime().toString() + "time_of_last_heartbeat": pDomain.timeOfLastHeartbeat?.toISOString(), + "time_of_last_heartbeat_s": pDomain.timeOfLastHeartbeat?.getTime().toString(), + "last_sender_key": pDomain.lastSenderKey, + "addr_of_first_contact": pDomain.iPAddrOfFirstContact, + "when_domain_entry_created": pDomain.whenCreated?.toISOString(), + "when_domain_entry_created_s": pDomain.whenCreated?.getTime().toString() }; -}; +} // Return the limited "user" info.. used by /api/v1/users export async function buildUserInfo(pAccount: AccountEntity): Promise { return { - 'accountId': pAccount.id, - 'id': pAccount.id, - 'username': pAccount.username, - 'images': await buildImageInfo(pAccount), - 'location': await buildLocationInfo(pAccount), + "accountId": pAccount.id, + "id": pAccount.id, + "username": pAccount.username, + "images": await buildImageInfo(pAccount), + "location": await buildLocationInfo(pAccount) }; -}; +} export async function buildImageInfo(pAccount: AccountEntity): Promise { const ret: VKeyedCollection = {}; - if (pAccount.imagesTiny) ret.tiny = pAccount.imagesTiny; - if (pAccount.imagesHero) ret.hero = pAccount.imagesHero; - if (pAccount.imagesThumbnail) ret.thumbnail = pAccount.imagesThumbnail; + if (pAccount.imagesTiny) { + ret.tiny = pAccount.imagesTiny; + } + if (pAccount.imagesHero) { + ret.hero = pAccount.imagesHero; + } + if (pAccount.imagesThumbnail) { + ret.thumbnail = pAccount.imagesThumbnail; + } return ret; -}; +} // Return the block of account information. // Used by several of the requests to return the complete account information. export async function buildAccountInfo(pReq: Request, pAccount: AccountEntity): Promise { return { - 'accountId': pAccount.id, - 'id': pAccount.id, - 'username': pAccount.username, - 'email': pAccount.email, - 'administrator': Accounts.isAdmin(pAccount), - 'enabled': Accounts.isEnabled(pAccount), - 'roles': pAccount.roles, - 'availability': pAccount.availability, - 'public_key': createSimplifiedPublicKey(pAccount.sessionPublicKey), - 'images': { - 'hero': pAccount.imagesHero, - 'tiny': pAccount.imagesTiny, - 'thumbnail': pAccount.imagesThumbnail + "accountId": pAccount.id, + "id": pAccount.id, + "username": pAccount.username, + "email": pAccount.email, + "administrator": Accounts.isAdmin(pAccount), + "enabled": Accounts.isEnabled(pAccount), + "roles": pAccount.roles, + "availability": pAccount.availability, + "public_key": createSimplifiedPublicKey(pAccount.sessionPublicKey), + "images": { + "hero": pAccount.imagesHero, + "tiny": pAccount.imagesTiny, + "thumbnail": pAccount.imagesThumbnail }, - 'profile_detail': pAccount.profileDetail, - 'location': await buildLocationInfo(pAccount), - 'friends': pAccount.friends, - 'connections': pAccount.connections, - 'when_account_created': pAccount.whenCreated?.toISOString(), - 'when_account_created_s': pAccount.whenCreated?.getTime().toString(), - 'time_of_last_heartbeat': pAccount.timeOfLastHeartbeat?.toISOString(), - 'time_of_last_heartbeat_s': pAccount.timeOfLastHeartbeat?.getTime().toString(), + "profile_detail": pAccount.profileDetail, + "location": await buildLocationInfo(pAccount), + "friends": pAccount.friends, + "connections": pAccount.connections, + "when_account_created": pAccount.whenCreated?.toISOString(), + "when_account_created_s": pAccount.whenCreated?.getTime().toString(), + "time_of_last_heartbeat": pAccount.timeOfLastHeartbeat?.toISOString(), + "time_of_last_heartbeat_s": pAccount.timeOfLastHeartbeat?.getTime().toString() }; -}; +} // Return the block of account information used as the account 'profile'. // Anyone can fetch a profile (if 'availability' is 'any') so not all info is returned export async function buildAccountProfile(pReq: Request, pAccount: AccountEntity): Promise { return { - 'accountId': pAccount.id, - 'id': pAccount.id, - 'username': pAccount.username, - 'images': { - 'hero': pAccount.imagesHero, - 'tiny': pAccount.imagesTiny, - 'thumbnail': pAccount.imagesThumbnail + "accountId": pAccount.id, + "id": pAccount.id, + "username": pAccount.username, + "images": { + "hero": pAccount.imagesHero, + "tiny": pAccount.imagesTiny, + "thumbnail": pAccount.imagesThumbnail }, - 'profile_detail': pAccount.profileDetail, - 'location': await buildLocationInfo(pAccount), - 'when_account_created': pAccount.whenCreated?.toISOString(), - 'when_account_created_s': pAccount.whenCreated?.getTime().toString(), - 'time_of_last_heartbeat': pAccount.timeOfLastHeartbeat?.toISOString(), - 'time_of_last_heartbeat_s': pAccount.timeOfLastHeartbeat?.getTime().toString(), + "profile_detail": pAccount.profileDetail, + "location": await buildLocationInfo(pAccount), + "when_account_created": pAccount.whenCreated?.toISOString(), + "when_account_created_s": pAccount.whenCreated?.getTime().toString(), + "time_of_last_heartbeat": pAccount.timeOfLastHeartbeat?.toISOString(), + "time_of_last_heartbeat_s": pAccount.timeOfLastHeartbeat?.getTime().toString() }; -}; +} // Return an object with the formatted place information // Pass the PlaceEntity and the place's domain if known. @@ -276,42 +286,42 @@ export async function buildPlaceInfo(pPlace: PlaceEntity, pDomain?: DomainEntity const aDomain = pDomain ?? await Domains.getDomainWithId(pPlace.domainId); if (aDomain) { ret.domain = await buildDomainInfo(aDomain); - }; - }; + } + } return ret; -}; +} // Return the basic information block for a Place export async function buildPlaceInfoSmall(pPlace: PlaceEntity, pDomain?: DomainEntity): Promise { - const ret: VKeyedCollection = { - 'placeId': pPlace.id, - 'id': pPlace.id, - 'name': pPlace.name, - 'displayName': pPlace.displayName, - 'visibility': pPlace.visibility ?? Visibility.OPEN, - 'address': await Places.getAddressString(pPlace), - 'path': pPlace.path, - 'description': pPlace.description, - 'maturity': pPlace.maturity ?? Maturity.UNRATED, - 'tags': pPlace.tags, - 'managers': await Places.getManagers(pPlace), - 'thumbnail': pPlace.thumbnail, - 'images': pPlace.images, - 'current_attendance': pPlace.currentAttendance ?? 0, - 'current_images': pPlace.currentImages, - 'current_info': pPlace.currentInfo, - 'current_last_update_time': pPlace.currentLastUpdateTime?.toISOString(), - 'current_last_update_time_s': pPlace.currentLastUpdateTime?.getTime().toString(), - 'last_activity_update': pPlace.lastActivity?.toISOString(), - 'last_activity_update_s': pPlace.lastActivity?.getTime().toString() + const ret: VKeyedCollection = { + "placeId": pPlace.id, + "id": pPlace.id, + "name": pPlace.name, + "displayName": pPlace.displayName, + "visibility": pPlace.visibility ?? Visibility.OPEN, + "address": await Places.getAddressString(pPlace), + "path": pPlace.path, + "description": pPlace.description, + "maturity": pPlace.maturity ?? Maturity.UNRATED, + "tags": pPlace.tags, + "managers": await Places.getManagers(pPlace), + "thumbnail": pPlace.thumbnail, + "images": pPlace.images, + "current_attendance": pPlace.currentAttendance ?? 0, + "current_images": pPlace.currentImages, + "current_info": pPlace.currentInfo, + "current_last_update_time": pPlace.currentLastUpdateTime?.toISOString(), + "current_last_update_time_s": pPlace.currentLastUpdateTime?.getTime().toString(), + "last_activity_update": pPlace.lastActivity?.toISOString(), + "last_activity_update_s": pPlace.lastActivity?.getTime().toString() }; return ret; -}; +} // Return an array of Places names that are associated with the passed domain export async function buildPlacesForDomain(pDomain: DomainEntity): Promise { const ret: any[] = []; - for await (const aPlace of Places.enumerateAsync(new GenericFilter({ 'domainId': pDomain.id }))) { + for await (const aPlace of Places.enumerateAsync(new GenericFilter({ "domainId": pDomain.id }))) { ret.push(await buildPlaceInfoSmall(aPlace, pDomain)); - }; + } return ret; -}; \ No newline at end of file +} diff --git a/src/route-tools/Validators.ts b/src/route-tools/Validators.ts index 0f088e0f..d8abc38f 100644 --- a/src/route-tools/Validators.ts +++ b/src/route-tools/Validators.ts @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Entity } from '@Entities/Entity'; +import { Entity } from "@Entities/Entity"; -import { FieldDefn, ValidateResponse } from '@Route-Tools/EntityFieldDefn'; +import { FieldDefn, ValidateResponse } from "@Route-Tools/EntityFieldDefn"; -import { SArray } from '@Tools/vTypes'; -import { IsNotNullOrEmpty, IsNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { SArray } from "@Tools/vTypes"; +import { IsNotNullOrEmpty, IsNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // ==== VALIDATE =========================================================================== // Functions that validate that the passed value is of the expected type. @@ -28,217 +27,205 @@ import { Logger } from '@Tools/Logging'; // Return Promise of 'true' if pValue is legal. // (These are async because some validation might require looking up some database info) export async function noValidation(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - return { valid: true }; -}; -export async function isStringValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - if (typeof(pValue) === 'string') { return { valid: true }; - }; - return { valid: false, reason: 'field value must be a string' }; -}; +} +export async function isStringValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { + if (typeof pValue === "string") { + return { valid: true }; + } + return { valid: false, reason: "field value must be a string" }; +} export async function isNumberValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - if (typeof(pValue) === 'number') { - return { valid: true }; - }; - return { valid: false, reason: 'field value must be a number' }; -}; + if (typeof pValue === "number") { + return { valid: true }; + } + return { valid: false, reason: "field value must be a number" }; +} export async function isBooleanValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - if (typeof(pValue) === 'boolean') { - return { valid: true }; - }; - return { valid: false, reason: 'field value must be a boolean' }; -}; + if (typeof pValue === "boolean") { + return { valid: true }; + } + return { valid: false, reason: "field value must be a boolean" }; +} export async function isPathValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - // Regexp to check format of "domainname/float,float,float/float,float,float,float" - // if (/^[\w\.:+_-]*\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?$/.test(pValue)) { - // Regexp to check format of "/float,float,float/float,float,float,float" - // This make a "path" just the position and rotation within a domain - if (typeof(pValue) === 'string') { - if (/^\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?$/.test(pValue)) { - return { valid: true }; - }; - }; - return { valid: false, reason: 'path must be a string of the form "/f,f,f/f,f,f,f' }; -}; + // Regexp to check format of "domainname/float,float,float/float,float,float,float" + // if (/^[\w\.:+_-]*\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?$/.test(pValue)) { + // Regexp to check format of "/float,float,float/float,float,float,float" + // This make a "path" just the position and rotation within a domain + if (typeof pValue === "string") { + if ((/^\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?$/).test(pValue)) { + return { valid: true }; + } + } + return { valid: false, reason: "path must be a string of the form \"/f,f,f/f,f,f,f" }; +} export async function isLongPathValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - if (typeof(pValue) === 'string') { - if (/^.*\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?$/.test(pValue)) { - return { valid: true }; - }; - }; - return { valid: false, reason: 'path must be a string of the form "optional-domain/f,f,f/f,f,f,f' }; -}; + if (typeof pValue === "string") { + if ((/^.*\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?\/-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?,-?\d+(\.\d*)?$/).test(pValue)) { + return { valid: true }; + } + } + return { valid: false, reason: "path must be a string of the form \"optional-domain/f,f,f/f,f,f,f" }; +} export async function isDateValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - if (pValue instanceof Date) { - return { valid: true }; - }; - return { valid: false, reason: 'field value must be a valid date string' }; -}; + if (pValue instanceof Date) { + return { valid: true }; + } + return { valid: false, reason: "field value must be a valid date string" }; +} export async function isObjectValidator(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - if (pValue instanceof Object) { - return { valid: true }; - }; - return { valid: false, reason: 'field value must resolve to an object' }; -}; + if (pValue instanceof Object) { + return { valid: true }; + } + return { valid: false, reason: "field value must resolve to an object" }; +} // verify the value is a string or a set/add/remove collection of string arrays. // This pairs with the SArray getter/setter which accepts a string (add), an // array of strings (set), or a manipulator (set/add/remove object). export async function isSArraySet(pField: FieldDefn, pEntity: Entity, pValue: any): Promise { - if (isValidSArraySet(pValue)) { - return { valid: true }; - }; - return { valid: false, reason: 'field value must be an array of strings' }; -}; + if (isValidSArraySet(pValue)) { + return { valid: true }; + } + return { valid: false, reason: "field value must be an array of strings" }; +} export function isValidSArraySet(pValue: any): boolean { - let ret = false; - if (typeof(pValue) === 'string') { + let ret = false; + if (typeof pValue === "string") { // If passed a string, setter will assume an 'add' operation - Logger.cdebug('field-setting', `isValidSArraySet: value is string and assuming set`); - ret = true; - } - else { - if (isValidSArray(pValue)) { - Logger.cdebug('field-setting', `isValidSArraySet: value is an SArray`); - // if we're passed an SArray, just presume a 'set' - ret = true; - } - else { - if (pValue && (pValue.set || pValue.add || pValue.remove)) { - Logger.cdebug('field-setting', `isValidSArraySet: object with one of the fields`); - let eachIsOk: boolean = true; + Logger.cdebug("field-setting", `isValidSArraySet: value is string and assuming set`); + ret = true; + } else if (isValidSArray(pValue)) { + Logger.cdebug("field-setting", `isValidSArraySet: value is an SArray`); + // if we're passed an SArray, just presume a 'set' + ret = true; + } else if (pValue && (pValue.set || pValue.add || pValue.remove)) { + Logger.cdebug("field-setting", `isValidSArraySet: object with one of the fields`); + let eachIsOk = true; if (eachIsOk && pValue.set) { - eachIsOk = typeof(pValue.set) === 'string' || isValidSArray(pValue.set); - Logger.cdebug('field-setting', `isValidSArraySet: pValue.set is ${eachIsOk}`); - }; + eachIsOk = typeof pValue.set === "string" || isValidSArray(pValue.set); + Logger.cdebug("field-setting", `isValidSArraySet: pValue.set is ${eachIsOk}`); + } if (eachIsOk && pValue.add) { - eachIsOk = typeof(pValue.add) === 'string' || isValidSArray(pValue.add); - Logger.cdebug('field-setting', `isValidSArraySet: pValue.add is ${eachIsOk}`); - }; + eachIsOk = typeof pValue.add === "string" || isValidSArray(pValue.add); + Logger.cdebug("field-setting", `isValidSArraySet: pValue.add is ${eachIsOk}`); + } if (eachIsOk && pValue.remove) { - eachIsOk = typeof(pValue.remove) === 'string' || isValidSArray(pValue.remove); - Logger.cdebug('field-setting', `isValidSArraySet: pValue.remove is ${eachIsOk}`); - }; + eachIsOk = typeof pValue.remove === "string" || isValidSArray(pValue.remove); + Logger.cdebug("field-setting", `isValidSArraySet: pValue.remove is ${eachIsOk}`); + } ret = eachIsOk; - }; - }; - }; - return ret; -}; + } + return ret; +} // Return 'true' is pValue is an array of strings export function isSArray(pField: FieldDefn, pEntity: Entity, pValue: any): boolean { - return isValidSArray(pValue); -}; + return isValidSArray(pValue); +} // Verify that the passed value is just an array of strings export function isValidSArray(pValue: any): boolean { - let ret = false; - if (pValue) { - if (Array.isArray(pValue)) { - let allStrings = true; - for (const val of pValue) { - if (typeof(val) !== 'string') { - allStrings = false; - break; - }; - }; - ret = allStrings; - }; - }; - Logger.cdebug('field-setting', `isSArray: "${JSON.stringify(pValue)}" is ${ret}`); - return ret; -}; + let ret = false; + if (pValue) { + if (Array.isArray(pValue)) { + let allStrings = true; + for (const val of pValue) { + if (typeof val !== "string") { + allStrings = false; + break; + } + } + ret = allStrings; + } + } + Logger.cdebug("field-setting", `isSArray: "${JSON.stringify(pValue)}" is ${ret}`); + return ret; +} // Accepts an SArray setter (see validator) and returns each value string found // in the setter. This is good for making sure all values match the type -export function *walkSArraySetter(pValue:any): Generator { - if (IsNotNullOrEmpty(pValue)) { - if (typeof(pValue) === 'string') { - yield (pValue as string); - } - else { - if (Array.isArray(pValue)) { - for (const aVal of pValue) { - yield aVal; - }; - } - else { - for (const manipulationSection of [ 'set', 'add', 'remove' ]) { - if (pValue.hasOwnProperty(manipulationSection)) { - const sectionVal = pValue[manipulationSection]; - if (Array.isArray(sectionVal)) { - for (const aVal of sectionVal) { +export function *walkSArraySetter(pValue: any): Generator { + if (IsNotNullOrEmpty(pValue)) { + if (typeof pValue === "string") { + yield pValue; + } else if (Array.isArray(pValue)) { + for (const aVal of pValue) { yield aVal; - }; } - else { - yield sectionVal; - }; - }; - }; - }; - }; - }; -}; + } else { + for (const manipulationSection of ["set", "add", "remove"]) { + if (pValue.hasOwnProperty(manipulationSection)) { + const sectionVal = pValue[manipulationSection]; + if (Array.isArray(sectionVal)) { + for (const aVal of sectionVal) { + yield aVal; + } + } else { + yield sectionVal; + } + } + } + } + } +} // Verify that all values in the passed SArray setting structure are // valid by applying the check function. // Returns 'true' if all values are valid. 'false' otherwise. export type ValidCheckFunction = (pVal: string) => Promise; export async function verifyAllSArraySetValues(pValue: any, pCheckFunction: ValidCheckFunction): Promise { - let ret = false; - if (IsNotNullOrEmpty(pCheckFunction) && isValidSArraySet(pValue)) { - ret = true; - for (const aVal of walkSArraySetter(pValue)) { - if (! await pCheckFunction(aVal)) { - ret = false; - break; - }; - }; - }; - return ret; -}; + let ret = false; + if (IsNotNullOrEmpty(pCheckFunction) && isValidSArraySet(pValue)) { + ret = true; + for (const aVal of walkSArraySetter(pValue)) { + if (!await pCheckFunction(aVal)) { + ret = false; + break; + } + } + } + return ret; +} // ======== GETTER ============================================================ export async function noGetter(pField: FieldDefn, pEntity: Entity): Promise { - Logger.error(`noGetter: attempt to get field ${pField.entity_field}`); -}; + Logger.error(`noGetter: attempt to get field ${pField.entity_field}`); +} // Return the field value. export async function simpleGetter(pField: FieldDefn, pEntity: Entity): Promise { - if (pEntity.hasOwnProperty(pField.entity_field)) { - return (pEntity as any)[pField.entity_field]; - }; - return undefined; -}; + if (pEntity.hasOwnProperty(pField.entity_field)) { + return (pEntity as any)[pField.entity_field]; + } + return undefined; +} // Return a date field as an ISO formatted string export async function dateStringGetter(pField: FieldDefn, pEntity: Entity): Promise { - if (pEntity.hasOwnProperty(pField.entity_field)) { - const dateVal: Date = (pEntity as any)[pField.entity_field]; - return dateVal ? dateVal.toISOString() : undefined; - }; - return undefined; -}; + if (pEntity.hasOwnProperty(pField.entity_field)) { + const dateVal: Date = (pEntity as any)[pField.entity_field]; + return dateVal ? dateVal.toISOString() : undefined; + } + return undefined; +} // ======== SETTER =============================================================== export async function noSetter(pField: FieldDefn, pEntity: Entity, pVal: any): Promise { - Logger.error(`noSetter: attempt to set field ${pField.entity_field}`); -}; + Logger.error(`noSetter: attempt to set field ${pField.entity_field}`); +} // Set the value of the field. // The different type of values require different manipulations. export async function simpleSetter(pField: FieldDefn, pEntity: Entity, pVal: any): Promise { - Logger.cdebug('field-setting', `simpleSetter: setting ${pField.entity_field}=>${JSON.stringify(pVal)}`); - (pEntity as any)[pField.entity_field] = pVal; -}; + Logger.cdebug("field-setting", `simpleSetter: setting ${pField.entity_field}=>${JSON.stringify(pVal)}`); + (pEntity as any)[pField.entity_field] = pVal; +} // A setter that will not set if the passed value is undefined, null, or a zero-length string export async function noOverwriteSetter(pField: FieldDefn, pEntity: Entity, pVal: any): Promise { - // Don't overwrite a value with a null or empty value. - if (IsNotNullOrEmpty(pVal)) { - if (Array.isArray(pVal)) { - if (pVal.length > 0) { - simpleSetter(pField, pEntity, pVal); - }; + // Don't overwrite a value with a null or empty value. + if (IsNotNullOrEmpty(pVal)) { + if (Array.isArray(pVal)) { + if (pVal.length > 0) { + simpleSetter(pField, pEntity, pVal); + } + } else { + simpleSetter(pField, pEntity, pVal); + } } - else { - simpleSetter(pField, pEntity, pVal); - }; - }; -}; +} // SArray setting. The passed value can be a string (which is added to the SValue) // or an object with the field 'set', 'add' or 'remove'. The values of the // latter fields can be a string or an array of strings. @@ -247,78 +234,69 @@ export async function noOverwriteSetter(pField: FieldDefn, pEntity: Entity, pVal // but this also accepts a string (presumes an "add") // a string array (presumes a "set") export async function sArraySetter(pField: FieldDefn, pEntity: Entity, pVal: any): Promise { - const fieldName = pField.entity_field; + const fieldName = pField.entity_field; - Logger.cdebug('field-setting', `sArraySetter: setting ${fieldName} with ${JSON.stringify(pVal)}`); - let val: string[] = (pEntity as any)[fieldName] ?? []; + Logger.cdebug("field-setting", `sArraySetter: setting ${fieldName} with ${JSON.stringify(pVal)}`); + let val: string[] = (pEntity as any)[fieldName] ?? []; - // Kludge for old entiries that are just a string rather than a proper SArray - if (typeof(val) === 'string') { - Logger.cdebug('field-setting', `sArraySetter: old value of ${fieldName} is just string. Making array`); - val = [ val ]; - }; + // Kludge for old entiries that are just a string rather than a proper SArray + if (typeof val === "string") { + Logger.cdebug("field-setting", `sArraySetter: old value of ${fieldName} is just string. Making array`); + val = [val]; + } - if (typeof(pVal) === 'string') { + if (typeof pVal === "string") { // If just passed a string, add it to the value SArray - Logger.cdebug('field-setting', `sArraySetter: adding string ${fieldName}`); - SArray.add(val, pVal); - } - else { - if (isSArray(pField, pEntity, pVal)) { - // If we're passed just an SArray, presume a 'set' operation - Logger.cdebug('field-setting', `sArraySetter: just passed SArray for ${fieldName}. Assuming set`); - val = pVal; - } - else { - if (typeof(pVal) === 'object' && (pVal.set || pVal.add || pVal.remove)) { + Logger.cdebug("field-setting", `sArraySetter: adding string ${fieldName}`); + SArray.add(val, pVal); + } else if (isSArray(pField, pEntity, pVal)) { + // If we're passed just an SArray, presume a 'set' operation + Logger.cdebug("field-setting", `sArraySetter: just passed SArray for ${fieldName}. Assuming set`); + val = pVal; + } else if (typeof pVal === "object" && (pVal.set || pVal.add || pVal.remove)) { if (pVal.set) { - // this relies on the validator to make sure we're passed clean values - val = (typeof(pVal.set) === 'string') ? [ pVal.set ] : pVal.set; - }; + // this relies on the validator to make sure we're passed clean values + val = typeof pVal.set === "string" ? [pVal.set] : pVal.set; + } if (pVal.add) { - if (Array.isArray(pVal.add)) { - Logger.cdebug('field-setting', `sArraySetter: adding array ${fieldName}=>${JSON.stringify(pVal.add)}`); - for (const aVal of pVal.add) { - SArray.add(val, aVal); - }; - } - else { - Logger.cdebug('field-setting', `sArraySetter: adding one ${fieldName}=>${JSON.stringify(pVal.add)}`); - SArray.add(val, pVal.add); - }; - }; + if (Array.isArray(pVal.add)) { + Logger.cdebug("field-setting", `sArraySetter: adding array ${fieldName}=>${JSON.stringify(pVal.add)}`); + for (const aVal of pVal.add) { + SArray.add(val, aVal); + } + } else { + Logger.cdebug("field-setting", `sArraySetter: adding one ${fieldName}=>${JSON.stringify(pVal.add)}`); + SArray.add(val, pVal.add); + } + } if (pVal.remove) { - if (Array.isArray(pVal.remove)) { - Logger.cdebug('field-setting', `sArraySetter: removing array ${fieldName}=>${JSON.stringify(pVal.remove)}`); - for (const aVal of pVal.remove) { - SArray.remove(val, aVal); - }; - } - else { - Logger.cdebug('field-setting', `sArraySetter: removing one ${fieldName}=>${JSON.stringify(pVal.remove)}`); - SArray.remove(val, pVal.remove); - }; - }; - } - else { - Logger.cdebug('field-setting', `sArraySetter: passed value that is not string, SArray, or manipulation object`); - }; - }; - }; - Logger.cdebug('field-setting', `sArraySetter: resulting ${fieldName}=>${JSON.stringify(val)}`); - (pEntity as any)[pField.entity_field] = cleanStringArray(val); -}; + if (Array.isArray(pVal.remove)) { + Logger.cdebug("field-setting", `sArraySetter: removing array ${fieldName}=>${JSON.stringify(pVal.remove)}`); + for (const aVal of pVal.remove) { + SArray.remove(val, aVal); + } + } else { + Logger.cdebug("field-setting", `sArraySetter: removing one ${fieldName}=>${JSON.stringify(pVal.remove)}`); + SArray.remove(val, pVal.remove); + } + } + } else { + Logger.cdebug("field-setting", `sArraySetter: passed value that is not string, SArray, or manipulation object`); + } + Logger.cdebug("field-setting", `sArraySetter: resulting ${fieldName}=>${JSON.stringify(val)}`); + (pEntity as any)[pField.entity_field] = cleanStringArray(val); +} // Clean up the object and make sure it is just an array of strings. // Passed what should be an array of strings and returns an array of only strings. function cleanStringArray(pValues: any): string[] { - const ret: string[] = []; - if (Array.isArray(pValues)) { - for (const val of pValues) { - if (typeof(val) === 'string') { - ret.push(val); - }; - }; - }; - return ret; -}; \ No newline at end of file + const ret: string[] = []; + if (Array.isArray(pValues)) { + for (const val of pValues) { + if (typeof val === "string") { + ret.push(val); + } + } + } + return ret; +} diff --git a/src/route-tools/middleware.ts b/src/route-tools/middleware.ts index cf6c3166..7bf38e7e 100755 --- a/src/route-tools/middleware.ts +++ b/src/route-tools/middleware.ts @@ -12,83 +12,79 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { Accounts } from '@Entities/Accounts'; -import { AccountEntity } from '@Entities/AccountEntity'; -import { Domains } from '@Entities/Domains'; -import { Tokens, TokenScope } from '@Entities/Tokens'; -import { AuthToken } from '@Entities/AuthToken'; -import { Sessions } from '@Entities/Sessions'; -import { Places } from '@Entities/Places'; +import { Accounts } from "@Entities/Accounts"; +import { AccountEntity } from "@Entities/AccountEntity"; +import { Domains } from "@Entities/Domains"; +import { Tokens, TokenScope } from "@Entities/Tokens"; +import { AuthToken } from "@Entities/AuthToken"; +import { Sessions } from "@Entities/Sessions"; +import { Places } from "@Entities/Places"; -import { RESTResponse } from '@Route-Tools/RESTResponse'; +import { RESTResponse } from "@Route-Tools/RESTResponse"; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { SArray, VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { SArray, VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; // MetaverseAPI middleware. // The request is a standard MetaverseAPI JSON-in and JSON-out request. // Start by decorating the request with the building class that is used to create the response // and setup the session. export const setupMetaverseAPI: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - req.vRestResp = new RESTResponse(req, resp); - if (req.socket) { - req.vSenderKey = `${req.socket.remoteAddress}:${req.socket.remotePort}`; + req.vRestResp = new RESTResponse(req, resp); + if (req.socket) { + req.vSenderKey = `${req.socket.remoteAddress}:${req.socket.remotePort}`; - req.vSession = Sessions.getSessionWithSenderKey(req.vSenderKey); - if (req.vSession) { - Sessions.touchSession(req.vSession); - } - else { - // No existing session for this request - req.vSession = Sessions.createSession(req.vSenderKey); - Sessions.addSession(req.vSession); - Logger.debug('setupMetaverseAPI: created new session for ' + req.vSenderKey); - }; + req.vSession = Sessions.getSessionWithSenderKey(req.vSenderKey); + if (req.vSession) { + Sessions.touchSession(req.vSession); + } else { + // No existing session for this request + req.vSession = Sessions.createSession(req.vSenderKey); + Sessions.addSession(req.vSession); + Logger.debug("setupMetaverseAPI: created new session for " + req.vSenderKey); + } - let authToken = req.vRestResp.getAuthToken(); - // If an authToken is not supplied in the header, it can be supplied in the query - if (IsNullOrEmpty(authToken)) { - if (req.query && req.query.access_token && typeof(req.query.access_token) === 'string') { - authToken = (req.query.access_token as string); - }; - }; - if (IsNotNullOrEmpty(authToken)) { - try { - req.vAuthToken = await Tokens.getTokenWithToken(authToken); - } - catch (err) { - Logger.error(`setupMetaverseAPI: exception in token lookup: ${err}`); - }; - }; - }; - next(); + let authToken = req.vRestResp.getAuthToken(); + // If an authToken is not supplied in the header, it can be supplied in the query + if (IsNullOrEmpty(authToken)) { + if (req.query && req.query.access_token && typeof req.query.access_token === "string") { + authToken = req.query.access_token; + } + } + if (IsNotNullOrEmpty(authToken)) { + try { + req.vAuthToken = await Tokens.getTokenWithToken(authToken); + } catch (err) { + Logger.error(`setupMetaverseAPI: exception in token lookup: ${err}`); + } + } + } + next(); }; // MetaverseAPI middleware. // Finish the API call by constructing the '{"status": "success", "data": RESPONSE }' JSON response // The request is terminated here by either 'resp.end()' or 'resp.json()'. export const finishMetaverseAPI: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - // Logger.debug('finishMetaverseAPI: enter'); - if (req.vRestResp) { - resp.statusCode = req.vRestResp.HTTPStatus; - const response = req.vRestResp.buildRESTResponse(); - if (response) { - Logger.cdebug('metaverseapi-response-detail', 'finishMetaverseAPI: response: ' + JSON.stringify(response)); - resp.json(response); + // Logger.debug('finishMetaverseAPI: enter'); + if (req.vRestResp) { + resp.statusCode = req.vRestResp.HTTPStatus; + const response = req.vRestResp.buildRESTResponse(); + if (response) { + Logger.cdebug("metaverseapi-response-detail", "finishMetaverseAPI: response: " + JSON.stringify(response)); + resp.json(response); + } else { + resp.end(); + } } - else { - resp.end(); - }; - }; }; // Like 'finishMetaverseAPI' but doesn't return the status/data JSON object but only @@ -97,17 +93,16 @@ export const finishMetaverseAPI: RequestHandler = async (req: Request, resp: Res // just JSON data. // The request is terminated here by either 'resp.end()' or 'resp.json()'. export const finishReturnData: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vRestResp) { - resp.statusCode = req.vRestResp.HTTPStatus; - const response = req.vRestResp.buildRESTResponse(); - if (response && response.data) { - Logger.cdebug('metaverseapi-response-detail', 'finishMetaverseAPI: response: ' + JSON.stringify(response.data)); - resp.json(response.data); + if (req.vRestResp) { + resp.statusCode = req.vRestResp.HTTPStatus; + const response = req.vRestResp.buildRESTResponse(); + if (response && response.data) { + Logger.cdebug("metaverseapi-response-detail", "finishMetaverseAPI: response: " + JSON.stringify(response.data)); + resp.json(response.data); + } else { + resp.end(); + } } - else { - resp.end(); - }; - }; }; // MetaverseAPI middleware. @@ -116,16 +111,16 @@ export const finishReturnData: RequestHandler = async (req: Request, resp: Respo // to an AccountEntity. // If account cannot be found, sets 'vAuthAccount' to undefined. export const accountFromAuthToken: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vRestResp) { - if (IsNotNullOrEmpty(req.vAuthToken)) { - req.vAuthAccount = await Accounts.getAccountWithId(req.vAuthToken.accountId); - if (IsNullOrEmpty(req.vAuthAccount)) { - req.vAccountError = 'Not logged in'; - Logger.debug('accountFromAuthToken: account lookup fail: authToken=' + req.vRestResp.getAuthToken()); - }; - }; - }; - next(); + if (req.vRestResp) { + if (IsNotNullOrEmpty(req.vAuthToken)) { + req.vAuthAccount = await Accounts.getAccountWithId(req.vAuthToken.accountId); + if (IsNullOrEmpty(req.vAuthAccount)) { + req.vAccountError = "Not logged in"; + Logger.debug("accountFromAuthToken: account lookup fail: authToken=" + req.vRestResp.getAuthToken()); + } + } + } + next(); }; // MetaverseAPI middleware. @@ -134,177 +129,175 @@ export const accountFromAuthToken: RequestHandler = async (req: Request, resp: R // Decorate the passed Request with 'vAccount' which points to a AccountEntity. // If account cannot be found or verified, 'vAccountError' is set with text explaining the error. export const accountFromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vRestResp) { - if (req.params && req.params.accountId && typeof(req.params.accountId) === 'string') { - const accountId = decodeURIComponent(req.params.accountId); - // Most of the account references are by username - req.vAccount = await Accounts.getAccountWithUsername(accountId); - if (IsNullOrEmpty(req.vAccount)) { - // If username didn't work, try by the accountId - req.vAccount = await Accounts.getAccountWithId(accountId); - }; - }; - }; - if (IsNullOrEmpty(req.vAccount)) { - req.vAccountError = 'AccountId does not match an account'; - }; - next(); + if (req.vRestResp) { + if (req.params && req.params.accountId && typeof req.params.accountId === "string") { + const accountId = decodeURIComponent(req.params.accountId); + // Most of the account references are by username + req.vAccount = await Accounts.getAccountWithUsername(accountId); + if (IsNullOrEmpty(req.vAccount)) { + // If username didn't work, try by the accountId + req.vAccount = await Accounts.getAccountWithId(accountId); + } + } + } + if (IsNullOrEmpty(req.vAccount)) { + req.vAccountError = "AccountId does not match an account"; + } + next(); }; // Check that 'vDomain' has access. Checks 'vDomainAPIKey' and request's authtoken. // If the domain in 'vDomain' does not check out, null out 'vDomain'. export const verifyDomainAccess: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vRestResp && req.vDomain) { - let verified: boolean = false; + let verified = false; - const authToken = req.vRestResp.getAuthToken(); - // Logger.debug(`verifyDomainAccess: domainId: ${req.vDomain.id}, authT: ${authToken}, apikey: ${req.vDomainAPIKey}`); + const authToken = req.vRestResp.getAuthToken(); + // Logger.debug(`verifyDomainAccess: domainId: ${req.vDomain.id}, authT: ${authToken}, apikey: ${req.vDomainAPIKey}`); - if (IsNullOrEmpty(authToken)) { + if (IsNullOrEmpty(authToken)) { // Auth token not available. See if APIKey does the trick - if (req.vDomain.apiKey === req.vDomainAPIKey) { - // APIKEY matches so create a fake AuthToken that works with checkAccessToEntity - // Note that this token does not get written to the DB - req.vAuthToken = await Tokens.createToken(req.vDomainAPIKey, [ TokenScope.DOMAIN ], 1); - verified = true; - }; - } - else { + if (req.vDomain.apiKey === req.vDomainAPIKey) { + // APIKEY matches so create a fake AuthToken that works with checkAccessToEntity + // Note that this token does not get written to the DB + req.vAuthToken = await Tokens.createToken(req.vDomainAPIKey, [TokenScope.DOMAIN], 1); + verified = true; + } + } else { // The request is being made with an authToken. // The token should be for a domain. - const aToken: AuthToken = await Tokens.getTokenWithToken(authToken); - if (aToken && aToken.accountId) { - if (SArray.has(aToken.scope, TokenScope.DOMAIN)) { - // As a hack, if the domain doesn't have a sponsor set, assign the account to the domain. - // This should be fixed with a request that does the initial domain-to-account assignment - if (IsNullOrEmpty(req.vDomain.sponsorAccountId)) { - const aAccount: AccountEntity = await Accounts.getAccountWithId(aToken.accountId); - if (aAccount) { - Logger.debug(`verifyDomainAccess: assigning domain ${req.vDomain.id} to account ${aAccount.id}`); - req.vDomain.sponsorAccountId = aAccount.id; - await Domains.updateEntityFields(req.vDomain, { 'sponsorAccountId': aAccount.id } ); - }; - }; - if (req.vDomain.sponsorAccountId === aToken.accountId) { - verified = true; - }; - } - else { - Logger.debug(`verifyDomainAccess: not DOMAIN token: ${authToken}`); - }; - }; - }; + const aToken: AuthToken = await Tokens.getTokenWithToken(authToken); + if (aToken && aToken.accountId) { + if (SArray.has(aToken.scope, TokenScope.DOMAIN)) { + // As a hack, if the domain doesn't have a sponsor set, assign the account to the domain. + // This should be fixed with a request that does the initial domain-to-account assignment + if (IsNullOrEmpty(req.vDomain.sponsorAccountId)) { + const aAccount: AccountEntity = await Accounts.getAccountWithId(aToken.accountId); + if (aAccount) { + Logger.debug(`verifyDomainAccess: assigning domain ${req.vDomain.id} to account ${aAccount.id}`); + req.vDomain.sponsorAccountId = aAccount.id; + await Domains.updateEntityFields(req.vDomain, { "sponsorAccountId": aAccount.id }); + } + } + if (req.vDomain.sponsorAccountId === aToken.accountId) { + verified = true; + } + } else { + Logger.debug(`verifyDomainAccess: not DOMAIN token: ${authToken}`); + } + } + } - if (!verified) { - req.vDomain = undefined; - req.vDomainError = 'Domain not authorized'; - }; - }; + if (!verified) { + req.vDomain = undefined; + req.vDomainError = "Domain not authorized"; + } + } next(); }; // Find domain apikey from JSON body and set as 'vDomainAPIKey' export const usernameFromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.params && req.params.username) { - if (typeof(req.params.username) === 'string') { - req.vUsername = decodeURIComponent(req.params.username); - }; - }; - next(); + if (req.params && req.params.username) { + if (typeof req.params.username === "string") { + req.vUsername = decodeURIComponent(req.params.username); + } + } + next(); }; // The request has a :domainId label that needs to be looked up and verified. // Decorate the passed Request with 'vDoamin' which points to a DomainEntity. // If domain cannot be found or verified, 'vDomainError' is set with text explaining the error. export const domainFromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - let domainId: string; - if (req.params && req.params.domainId) { - if (typeof(req.params.domainId) === 'string') { - domainId = req.params.domainId; - req.vDomain = await Domains.getDomainWithId(domainId); - }; - }; - if (IsNullOrEmpty(req.vDomain)) { - req.vDomainError = 'DomainId does not match a domain'; - Logger.error(`domainFromParams: wanted domain ${domainId} but not found`); - }; - next(); + let domainId: string; + if (req.params && req.params.domainId) { + if (typeof req.params.domainId === "string") { + domainId = req.params.domainId; + req.vDomain = await Domains.getDomainWithId(domainId); + } + } + if (IsNullOrEmpty(req.vDomain)) { + req.vDomainError = "DomainId does not match a domain"; + Logger.error(`domainFromParams: wanted domain ${domainId} but not found`); + } + next(); }; // Look for :placeId label and lookup and set req.vPlace // The accepts either the 'placeId' or the name of the place export const placeFromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.params && req.params.placeId) { - if (typeof(req.params.placeId) === 'string') { - let aPlace = await Places.getPlaceWithId(req.params.placeId); - if (IsNullOrEmpty(aPlace)) { - aPlace = await Places.getPlaceWithName(decodeURIComponent(req.params.placeId)); - }; - if (IsNotNullOrEmpty(aPlace)) { - req.vPlace = aPlace; - }; - }; - }; - next(); + if (req.params && req.params.placeId) { + if (typeof req.params.placeId === "string") { + let aPlace = await Places.getPlaceWithId(req.params.placeId); + if (IsNullOrEmpty(aPlace)) { + aPlace = await Places.getPlaceWithName(decodeURIComponent(req.params.placeId)); + } + if (IsNotNullOrEmpty(aPlace)) { + req.vPlace = aPlace; + } + } + } + next(); }; // Find domain apikey from JSON body and set as 'vDomainAPIKey' export const domainAPIkeyFromBody: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.body && req.body.domain && req.body.domain.api_key) { - if (typeof(req.body.domain.api_key) === 'string') { - req.vDomainAPIKey = req.body.domain.api_key; - }; - }; - next(); + if (req.body && req.body.domain && req.body.domain.api_key) { + if (typeof req.body.domain.api_key === "string") { + req.vDomainAPIKey = req.body.domain.api_key; + } + } + next(); }; // Find domain apikey from previously parsed multi-part form body and set as 'vDomainAPIKey' export const domainAPIkeyFromMultipart: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.body && req.body.api_key) { - if (typeof(req.body.api_key) === 'string') { - req.vDomainAPIKey = req.body.api_key; - }; - }; - next(); + if (req.body && req.body.api_key) { + if (typeof req.body.api_key === "string") { + req.vDomainAPIKey = req.body.api_key; + } + } + next(); }; // MetaverseAPI middleware. // The request has a :tokenId label that is returned in 'vTokenId'. export const tokenFromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.params && req.params.tokenId) { - if (typeof(req.params.tokenId) === 'string') { - req.vTokenId = req.params.tokenId; - }; - }; - next(); + if (req.params && req.params.tokenId) { + if (typeof req.params.tokenId === "string") { + req.vTokenId = req.params.tokenId; + } + } + next(); }; // MetaverseAPI middleware. // The request has a :param1 label that is returned in 'vParam1'. export const param1FromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.params && req.params.param1) { - if (typeof(req.params.param1) === 'string') { - req.vParam1 = decodeURIComponent(req.params.param1); - }; - }; - next(); + if (req.params && req.params.param1) { + if (typeof req.params.param1 === "string") { + req.vParam1 = decodeURIComponent(req.params.param1); + } + } + next(); }; // MetaverseAPI middleware. // The request has a :param2 label that is returned in 'vParam2'. export const param2FromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.params && req.params.param2) { - if (typeof(req.params.param2) === 'string') { - req.vParam2 = decodeURIComponent(req.params.param2); - }; - }; - next(); + if (req.params && req.params.param2) { + if (typeof req.params.param2 === "string") { + req.vParam2 = decodeURIComponent(req.params.param2); + } + } + next(); }; // MetaverseAPI middleware. // The request has a :param3 label that is returned in 'vParam3'. export const param3FromParams: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.params && req.params.param3) { - if (typeof(req.params.param3) === 'string') { - req.vParam3 = decodeURIComponent(req.params.param3); - }; - }; - next(); -}; \ No newline at end of file + if (req.params && req.params.param3) { + if (typeof req.params.param3 === "string") { + req.vParam3 = decodeURIComponent(req.params.param3); + } + } + next(); +}; diff --git a/src/routes-first/debugg.ts b/src/routes-first/debugg.ts index 2fa6d606..4943ea7d 100755 --- a/src/routes-first/debugg.ts +++ b/src/routes-first/debugg.ts @@ -12,20 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { Config } from '@Base/config'; -import { Logger } from '@Tools/Logging'; +import { Config } from "@Base/config"; +import { Logger } from "@Tools/Logging"; const procAllDebug: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - Logger.cdebug('request-detail', `procAllDebug: ${req.method} ${req.url}`); - Logger.cdebug('request-body', `procAllDebug: body: ` + JSON.stringify(req.body)); + Logger.cdebug("request-detail", `procAllDebug: ${req.method} ${req.url}`); + Logger.cdebug("request-body", `procAllDebug: body: ` + JSON.stringify(req.body)); next(); }; export const name = "Debug"; export const router = Router() - .use(procAllDebug); \ No newline at end of file + .use(procAllDebug); diff --git a/src/routes-first/stats.ts b/src/routes-first/stats.ts index 4ff94bcf..7e27b0e1 100755 --- a/src/routes-first/stats.ts +++ b/src/routes-first/stats.ts @@ -12,17 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { Monitoring } from '@Monitoring/Monitoring'; +import { Monitoring } from "@Monitoring/Monitoring"; -import { Config } from '@Base/config'; -import { Logger } from '@Tools/Logging'; +import { Config } from "@Base/config"; +import { Logger } from "@Tools/Logging"; const procAllStats: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - Monitoring.event('apiRequests', 1); + Monitoring.event("apiRequests", 1); // TODO: maybe have specific request stats... next(); }; @@ -30,4 +29,4 @@ const procAllStats: RequestHandler = async (req: Request, resp: Response, next: export const name = "Request stats"; export const router = Router() - .use(procAllStats); + .use(procAllStats); diff --git a/src/routes-last/errors.ts b/src/routes-last/errors.ts index 70b463cd..a6affa66 100755 --- a/src/routes-last/errors.ts +++ b/src/routes-last/errors.ts @@ -12,32 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, ErrorRequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, ErrorRequestHandler, Request, Response, NextFunction } from "express"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; -import createError, { HttpError } from 'http-errors'; +import createError, { HttpError } from "http-errors"; export const name = "ERROR"; export const router = Router(); // catch 404 and forward to error handler -router.use( (req: Request, res: Response, next: NextFunction) => { - Logger.debug('procCreateError: no response so returning 404'); +router.use((req: Request, res: Response, next: NextFunction) => { + Logger.debug("procCreateError: no response so returning 404"); next(createError(404)); }); // error handler -router.use( (err: HttpError, req: Request, res: Response, next: NextFunction) => { - Logger.debug('procFinalError: generating final error'); +router.use((err: HttpError, req: Request, res: Response, next: NextFunction) => { + Logger.debug("procFinalError: generating final error"); // set locals, only providing error in development res.locals.message = err.message; - res.locals.error = req.app.get('env') === 'development' ? err : {}; + res.locals.error = req.app.get("env") === "development" ? err : {}; // render the error page res.status(err.status || 500); res.end(); -}); \ No newline at end of file +}); diff --git a/src/routes/api/maint/makeAdmin.ts b/src/routes/api/maint/makeAdmin.ts index 5264bce6..a52d1d2c 100755 --- a/src/routes/api/maint/makeAdmin.ts +++ b/src/routes/api/maint/makeAdmin.ts @@ -12,57 +12,52 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; -import { Logger } from '@Tools/Logging'; -import { Accounts } from '@Entities/Accounts'; -import { Tokens } from '@Entities/Tokens'; -import { IsNotNullOrEmpty } from '@Tools/Misc'; -import { Roles } from '@Entities/Sets/Roles'; +import { Logger } from "@Tools/Logging"; +import { Accounts } from "@Entities/Accounts"; +import { Tokens } from "@Entities/Tokens"; +import { IsNotNullOrEmpty } from "@Tools/Misc"; +import { Roles } from "@Entities/Sets/Roles"; -import { SArray, VKeyedCollection } from '@Tools/vTypes'; +import { SArray, VKeyedCollection } from "@Tools/vTypes"; // Temporary maint function to create the first admin account const procMakeAdmin: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - const adminAccountName = Config["metaverse-server"]["base-admin-account"] ?? 'wilma'; + const adminAccountName = Config["metaverse-server"]["base-admin-account"] ?? "wilma"; if (req.vRestResp) { const adminAccount = await Accounts.getAccountWithUsername(adminAccountName); if (IsNotNullOrEmpty(adminAccount)) { if (SArray.has(adminAccount.roles, Roles.ADMIN)) { Logger.debug(`procMakeAdmin: ${adminAccountName} already has role "admin"`); - } - else { + } else { const updates: VKeyedCollection = {}; const adminToken = Tokens.createSpecialAdminToken(); - const success = await Accounts.setField(adminToken, adminAccount, 'roles', { 'add': Roles.ADMIN }, adminAccount, updates); + const success = await Accounts.setField(adminToken, adminAccount, "roles", { "add": Roles.ADMIN }, adminAccount, updates); if (success.valid) { await Accounts.updateEntityFields(adminAccount, updates); + } else { + req.vRestResp.respondFailure("could not set admin: " + success.reason); } - else { - req.vRestResp.respondFailure('could not set admin: ' + success.reason); - }; - }; - } - else { + } + } else { Logger.error(`procMakeAdmin: could not fetch account "${adminAccountName}"`); - req.vRestResp.respondFailure('No account named admin account name exists'); - }; - }; + req.vRestResp.respondFailure("No account named admin account name exists"); + } + } next(); }; -export const name = '/api/maint/makeAdmin'; +export const name = "/api/maint/makeAdmin"; export const router = Router(); -router.get('/api/maint/makeAdmin', [ setupMetaverseAPI, - procMakeAdmin, - finishMetaverseAPI ] ); - - - +router.get("/api/maint/makeAdmin", [ + setupMetaverseAPI, + procMakeAdmin, + finishMetaverseAPI +]); diff --git a/src/routes/api/maint/places.ts b/src/routes/api/maint/places.ts index c08e8cc2..6c3870c5 100644 --- a/src/routes/api/maint/places.ts +++ b/src/routes/api/maint/places.ts @@ -12,26 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; +import Config from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { CriteriaFilter } from '@Entities/EntityFilters/CriteriaFilter'; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { PlaceFilterInfo } from '@Entities/EntityFilters/PlaceFilterInfo'; -import { buildPlaceInfo } from '@Route-Tools/Util'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { CriteriaFilter } from "@Entities/EntityFilters/CriteriaFilter"; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { PlaceFilterInfo } from "@Entities/EntityFilters/PlaceFilterInfo"; +import { buildPlaceInfo } from "@Route-Tools/Util"; -import { Domains } from '@Entities/Domains'; -import { Places } from '@Entities/Places'; -import { PlaceEntity } from '@Entities/PlaceEntity'; -import { Accounts } from '@Entities/Accounts'; -import { Maturity } from '@Entities/Sets/Maturity'; +import { Domains } from "@Entities/Domains"; +import { Places } from "@Entities/Places"; +import { PlaceEntity } from "@Entities/PlaceEntity"; +import { Accounts } from "@Entities/Accounts"; +import { Maturity } from "@Entities/Sets/Maturity"; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // Return places that don't have an associated domain const procPlacesUnhooked: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -47,21 +46,19 @@ const procPlacesUnhooked: RequestHandler = async (req: Request, resp: Response, const places: any[] = []; for await (const aPlace of unhookedPlaces(placer, pager)) { places.push(await buildPlaceInfo(aPlace)); - }; + } req.vRestResp.Data = { - 'places': places, + places, // Maturity catagories added so client knows what is defined in the metaverse-server - 'maturity-categories': Maturity.MaturityCategories + "maturity-categories": Maturity.MaturityCategories }; + } else { + req.vRestResp.respondFailure("not admin"); } - else { - req.vRestResp.respondFailure('not admin'); - }; + } else { + req.vRestResp.respondFailure("must have account for access"); } - else { - req.vRestResp.respondFailure('must have account for access'); - }; placer.addResponseFields(req); pager.addResponseFields(req); @@ -81,15 +78,13 @@ const procPlacesDeleteUnhooked: RequestHandler = async (req: Request, resp: Resp for await (const aPlace of unhookedPlaces(placer, pager)) { Logger.info(`DeleteUnhooked: deleting place ${aPlace.name} for having no domain`); Places.removePlace(aPlace); - }; + } + } else { + req.vRestResp.respondFailure("not admin"); } - else { - req.vRestResp.respondFailure('not admin'); - }; + } else { + req.vRestResp.respondFailure("must have account for access"); } - else { - req.vRestResp.respondFailure('must have account for access'); - }; placer.addResponseFields(req); pager.addResponseFields(req); @@ -102,9 +97,9 @@ async function *unhookedPlaces(pPager: CriteriaFilter, pInfoer?: CriteriaFilter) const aDomain = await Domains.getDomainWithId(aPlace.domainId); if (IsNullOrEmpty(aDomain) || IsNullOrEmpty(aDomain.networkAddr)) { yield aPlace; - }; - }; -}; + } + } +} // Return places that haven't pinged in a while const procPlacesInactive: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -119,21 +114,19 @@ const procPlacesInactive: RequestHandler = async (req: Request, resp: Response, const places: any[] = []; for await (const aPlace of inactivePlaces(placer, pager)) { places.push(await buildPlaceInfo(aPlace)); - }; + } req.vRestResp.Data = { - 'places': places, + places, // Maturity catagories added so client knows what is defined in the metaverse-server - 'maturity-categories': Maturity.MaturityCategories + "maturity-categories": Maturity.MaturityCategories }; + } else { + req.vRestResp.respondFailure("not admin"); } - else { - req.vRestResp.respondFailure('not admin'); - }; + } else { + req.vRestResp.respondFailure("must have account for access"); } - else { - req.vRestResp.respondFailure('must have account for access'); - }; placer.addResponseFields(req); pager.addResponseFields(req); @@ -153,15 +146,13 @@ const procPlacesDeleteInactive: RequestHandler = async (req: Request, resp: Resp for await (const aPlace of inactivePlaces(placer, pager)) { Logger.info(`DeleteInactive: deleting place ${aPlace.name} for being inactive`); Places.removePlace(aPlace); - }; + } + } else { + req.vRestResp.respondFailure("not admin"); } - else { - req.vRestResp.respondFailure('not admin'); - }; + } else { + req.vRestResp.respondFailure("must have account for access"); } - else { - req.vRestResp.respondFailure('must have account for access'); - }; placer.addResponseFields(req); pager.addResponseFields(req); @@ -174,31 +165,39 @@ async function *inactivePlaces(pPager: CriteriaFilter, pInfoer?: CriteriaFilter) for await (const aPlace of Places.enumerateAsync(pPager, pInfoer)) { if (IsNotNullOrEmpty(aPlace.lastActivity) || aPlace.lastActivity < inactivePlaceTime) { yield aPlace; - }; - }; -}; + } + } +} -export const name = '/api/maint/places/'; +export const name = "/api/maint/places/"; export const router = Router(); // Return places that don't have an associated domain -router.get('/api/maint/places/unhooked', [ setupMetaverseAPI, - accountFromAuthToken, - procPlacesUnhooked, - finishMetaverseAPI ] ); +router.get("/api/maint/places/unhooked", [ + setupMetaverseAPI, + accountFromAuthToken, + procPlacesUnhooked, + finishMetaverseAPI +]); // Delete the places that are unhooked -router.delete('/api/maint/places/unhooked', [ setupMetaverseAPI, - accountFromAuthToken, - procPlacesDeleteUnhooked, - finishMetaverseAPI ] ); +router["delete"]("/api/maint/places/unhooked", [ + setupMetaverseAPI, + accountFromAuthToken, + procPlacesDeleteUnhooked, + finishMetaverseAPI +]); // Return places that haven't pinged in a while -router.get('/api/maint/places/inactive', [ setupMetaverseAPI, - accountFromAuthToken, - procPlacesInactive, - finishMetaverseAPI ] ); +router.get("/api/maint/places/inactive", [ + setupMetaverseAPI, + accountFromAuthToken, + procPlacesInactive, + finishMetaverseAPI +]); // Delete the places that are inactive -router.delete('/api/maint/places/inactive', [ setupMetaverseAPI, - accountFromAuthToken, - procPlacesDeleteInactive, - finishMetaverseAPI ] ); +router["delete"]("/api/maint/places/inactive", [ + setupMetaverseAPI, + accountFromAuthToken, + procPlacesDeleteInactive, + finishMetaverseAPI +]); diff --git a/src/routes/api/maint/raw.ts b/src/routes/api/maint/raw.ts index e92f3c60..a9d3c358 100755 --- a/src/routes/api/maint/raw.ts +++ b/src/routes/api/maint/raw.ts @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { param1FromParams, param2FromParams, param3FromParams } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { param1FromParams, param2FromParams, param3FromParams } from "@Route-Tools/middleware"; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; -import { getObject } from '@Tools/Db'; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; +import { getObject } from "@Tools/Db"; -import { Accounts } from '@Entities/Accounts'; -import { SimpleObject } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { Accounts } from "@Entities/Accounts"; +import { SimpleObject } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // A maint function for admin account that lets one get any object in any collection const procMaintRaw: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -36,30 +35,26 @@ const procMaintRaw: RequestHandler = async (req: Request, resp: Response, next: const value = req.vParam3; const criteria = new GenericFilter(SimpleObject(field, value)); req.vRestResp.Data = await getObject(collection, criteria); + } else { + req.vRestResp.respondFailure("parameters missing"); } - else { - req.vRestResp.respondFailure('parameters missing'); - }; + } else { + req.vRestResp.respondFailure("not admin"); } - else { - req.vRestResp.respondFailure('not admin'); - }; - }; + } next(); }; -export const name = '/api/maint/raw/'; +export const name = "/api/maint/raw/"; export const router = Router(); -router.get('/api/maint/raw/:param1/:param2/:param3', [ setupMetaverseAPI, - accountFromAuthToken, - param1FromParams, - param2FromParams, - param3FromParams, - procMaintRaw, - finishMetaverseAPI ] ); - - - - +router.get("/api/maint/raw/:param1/:param2/:param3", [ + setupMetaverseAPI, + accountFromAuthToken, + param1FromParams, + param2FromParams, + param3FromParams, + procMaintRaw, + finishMetaverseAPI +]); diff --git a/src/routes/api/maint/testSarray.ts b/src/routes/api/maint/testSarray.ts index 5e473497..9fffae95 100755 --- a/src/routes/api/maint/testSarray.ts +++ b/src/routes/api/maint/testSarray.ts @@ -12,86 +12,82 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; -import { Availability } from '@Entities/Sets/Availability'; -import { isValidSArraySet, isValidSArray, verifyAllSArraySetValues, isPathValidator } from '@Route-Tools/Validators'; +import { Availability } from "@Entities/Sets/Availability"; +import { isValidSArraySet, isValidSArray, verifyAllSArraySetValues, isPathValidator } from "@Route-Tools/Validators"; -import { SArray } from '@Tools/vTypes'; -import { IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { SArray } from "@Tools/vTypes"; +import { IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // Temporary maint function to test some internal functions. // Someday there should be a real test suite. const proctestsarray: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { req.vRestResp.Data = { - 'isValidSArray': { - 'undefined': isValidSArray(undefined), - 'null': isValidSArray(null), - 'empty object': isValidSArray({}), - 'empty array': isValidSArray([]), - 'one string': isValidSArray(['one']), - 'two strings': isValidSArray(['one', 'two']), - 'five strings': isValidSArray(['one', 'two', 'three', 'four', 'five']), - 'one number': isValidSArray(['one', 2, 'three', 'four', 'five']), - 'an object': isValidSArray(['one', { 'stuff': 'more stuff' }, 'three', 'four', 'five']) + "isValidSArray": { + "undefined": isValidSArray(undefined), + "null": isValidSArray(null), + "empty object": isValidSArray({}), + "empty array": isValidSArray([]), + "one string": isValidSArray(["one"]), + "two strings": isValidSArray(["one", "two"]), + "five strings": isValidSArray(["one", "two", "three", "four", "five"]), + "one number": isValidSArray(["one", 2, "three", "four", "five"]), + "an object": isValidSArray(["one", { "stuff": "more stuff" }, "three", "four", "five"]) }, - 'isValidSArraySet': { - 'undefined': isValidSArraySet(undefined), - 'null': isValidSArraySet(null), - 'empty object': isValidSArraySet({}), - 'string': isValidSArraySet('new value'), - 'set string': isValidSArraySet({ 'set': 'new value' }), - 'set array': isValidSArraySet({ 'set': [ 'one', 'two', 'three' ] }), - 'add string': isValidSArraySet({ 'add': 'new value' }), - 'add array': isValidSArraySet({ 'add': [ 'one', 'two', 'three' ] }), - 'remove string': isValidSArraySet({ 'remove': 'new value' }), - 'remove array': isValidSArraySet({ 'remove': [ 'one', 'two', 'three' ] }), - 'set and remove': isValidSArraySet({ 'set': ['one', 'two'], 'remove': [ 'three', 'four']}), - 'extra op': isValidSArraySet({ 'set': ['one', 'two'], 'diddle': [ 'three', 'four']}) + "isValidSArraySet": { + "undefined": isValidSArraySet(undefined), + "null": isValidSArraySet(null), + "empty object": isValidSArraySet({}), + "string": isValidSArraySet("new value"), + "set string": isValidSArraySet({ "set": "new value" }), + "set array": isValidSArraySet({ "set": ["one", "two", "three"] }), + "add string": isValidSArraySet({ "add": "new value" }), + "add array": isValidSArraySet({ "add": ["one", "two", "three"] }), + "remove string": isValidSArraySet({ "remove": "new value" }), + "remove array": isValidSArraySet({ "remove": ["one", "two", "three"] }), + "set and remove": isValidSArraySet({ "set": ["one", "two"], "remove": ["three", "four"] }), + "extra op": isValidSArraySet({ "set": ["one", "two"], "diddle": ["three", "four"] }) }, - 'verifyAllSArraySetValues': { - 'undefined': await verifyAllSArraySetValues(undefined, undefined), - 'null': await verifyAllSArraySetValues(null, undefined), - 'null function': await verifyAllSArraySetValues(null, null), - 'good values': await verifyAllSArraySetValues({ - 'set': [ 'none', 'friends' ], - 'remove': 'connections' + "verifyAllSArraySetValues": { + "undefined": await verifyAllSArraySetValues(undefined, undefined), + "null": await verifyAllSArraySetValues(null, undefined), + "null function": await verifyAllSArraySetValues(null, null), + "good values": await verifyAllSArraySetValues({ + "set": ["none", "friends"], + "remove": "connections" }, Availability.KnownAvailability), - 'one bad value': await verifyAllSArraySetValues({ - 'set': [ 'none', 'friends' ], - 'remove': [ 'connections', 'frog' ] + "one bad value": await verifyAllSArraySetValues({ + "set": ["none", "friends"], + "remove": ["connections", "frog"] }, Availability.KnownAvailability) }, - 'checkPathValidation': { - 'good path 1': await isPathValidator(undefined, undefined, '/0,0,0/0,0,0,1'), - 'good path 2': await isPathValidator(undefined, undefined, '/23.5,-44.6,22/0.5639,-0.3355,0.32,0.33'), - 'good path 3': await isPathValidator(undefined, undefined, 'domainname/23.5,-44.6,22/0.5639,-0.3355,0.32,0.33'), - 'good path 4': await isPathValidator(undefined, undefined, '/-33,20,44/-0.447,0.3232,0.031,1'), - 'good path 5': await isPathValidator(undefined, undefined, '/4,5,6/0.343,0.444,-34,1.0'), - 'bad path 1': await isPathValidator(undefined, undefined, '0,0,0/0,0,0,1'), - 'bad path 2': await isPathValidator(undefined, undefined, '/X,0,0/0,0,0,1'), - 'bad path 3': await isPathValidator(undefined, undefined, '/0,0,0/0,0,0,1/'), + "checkPathValidation": { + "good path 1": await isPathValidator(undefined, undefined, "/0,0,0/0,0,0,1"), + "good path 2": await isPathValidator(undefined, undefined, "/23.5,-44.6,22/0.5639,-0.3355,0.32,0.33"), + "good path 3": await isPathValidator(undefined, undefined, "domainname/23.5,-44.6,22/0.5639,-0.3355,0.32,0.33"), + "good path 4": await isPathValidator(undefined, undefined, "/-33,20,44/-0.447,0.3232,0.031,1"), + "good path 5": await isPathValidator(undefined, undefined, "/4,5,6/0.343,0.444,-34,1.0"), + "bad path 1": await isPathValidator(undefined, undefined, "0,0,0/0,0,0,1"), + "bad path 2": await isPathValidator(undefined, undefined, "/X,0,0/0,0,0,1"), + "bad path 3": await isPathValidator(undefined, undefined, "/0,0,0/0,0,0,1/") } }; next(); }; -export const name = '/api/maint/testSArray'; +export const name = "/api/maint/testSArray"; export const router = Router(); -router.get('/api/maint/testSArray', [ setupMetaverseAPI, - proctestsarray, - finishMetaverseAPI ] ); - - - - - +router.get("/api/maint/testSArray", [ + setupMetaverseAPI, + proctestsarray, + finishMetaverseAPI +]); diff --git a/src/routes/api/metaverse_info.ts b/src/routes/api/metaverse_info.ts index b30aed99..7399abe5 100755 --- a/src/routes/api/metaverse_info.ts +++ b/src/routes/api/metaverse_info.ts @@ -12,25 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, finishReturnData } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, finishReturnData } from "@Route-Tools/middleware"; -import { Config, readInJSON } from '@Base/config'; +import { Config, readInJSON } from "@Base/config"; -import { Logger } from '@Tools/Logging'; -import { IsNotNullOrEmpty } from '@Tools/Misc'; +import { Logger } from "@Tools/Logging"; +import { IsNotNullOrEmpty } from "@Tools/Misc"; -import deepmerge from 'deepmerge'; +import deepmerge from "deepmerge"; const procMetaverseInfo: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - let data:any = { - 'metaverse_name': Config.metaverse["metaverse-name"], - 'metaverse_nick_name': Config.metaverse["metaverse-nick-name"], - 'metaverse_url': Config.metaverse["metaverse-server-url"], - 'ice_server_url': Config.metaverse["default-ice-server-url"], - 'metaverse_server_version': Config.server["server-version"], + let data: any = { + "metaverse_name": Config.metaverse["metaverse-name"], + "metaverse_nick_name": Config.metaverse["metaverse-nick-name"], + "metaverse_url": Config.metaverse["metaverse-server-url"], + "ice_server_url": Config.metaverse["default-ice-server-url"], + "metaverse_server_version": Config.server["server-version"] }; // If the additional information file exists, read and include the contents @@ -40,12 +39,11 @@ const procMetaverseInfo: RequestHandler = async (req: Request, resp: Response, n const additional = await readInJSON(additionUrl); if (IsNotNullOrEmpty(additional)) { data = deepmerge(data, additional); - }; - }; - } - catch (err) { + } + } + } catch (err) { Logger.error(`procMetaverseInfo: exception reading additional info file: ${err}`); - }; + } req.vRestResp.Data = data; next(); @@ -55,9 +53,13 @@ export const name = "/api/v1/metaverse_info"; export const router = Router(); -router.get( '/api/metaverse_info', [ setupMetaverseAPI, - procMetaverseInfo, - finishReturnData ] ); -router.get( '/api/v1/metaverse_info', [ setupMetaverseAPI, - procMetaverseInfo, - finishReturnData ] ); \ No newline at end of file +router.get("/api/metaverse_info", [ + setupMetaverseAPI, + procMetaverseInfo, + finishReturnData +]); +router.get("/api/v1/metaverse_info", [ + setupMetaverseAPI, + procMetaverseInfo, + finishReturnData +]); diff --git a/src/routes/api/v1/account/accountId.ts b/src/routes/api/v1/account/accountId.ts index 4799acb9..27739e93 100755 --- a/src/routes/api/v1/account/accountId.ts +++ b/src/routes/api/v1/account/accountId.ts @@ -12,42 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken, accountFromParams } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken, accountFromParams } from "@Route-Tools/middleware"; -import { Perm } from '@Route-Tools/Perm'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; -import { buildAccountInfo } from '@Route-Tools/Util'; +import { Perm } from "@Route-Tools/Perm"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; +import { buildAccountInfo } from "@Route-Tools/Util"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; // metaverseServerApp.use(express.urlencoded({ extended: false })); const procGetAccountId: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vAccount) { - if (await checkAccessToEntity(req.vAuthToken, req.vAccount, [ Perm.OWNER, Perm.ADMIN ])) { + if (await checkAccessToEntity(req.vAuthToken, req.vAccount, [Perm.OWNER, Perm.ADMIN])) { req.vRestResp.Data = { - account: await buildAccountInfo(req, req.vAccount) + account: await buildAccountInfo(req, req.vAccount) }; + } else { + req.vRestResp.respondFailure("Unauthorized"); } - else { - req.vRestResp.respondFailure('Unauthorized'); - }; + } else { + req.vRestResp.respondFailure("Target account not found"); } - else { - req.vRestResp.respondFailure('Target account not found'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; @@ -59,32 +55,30 @@ const procPostAccountId: RequestHandler = async (req: Request, resp: Response, n if (req.vAccount) { const valuesToSet = req.body.accounts; const updates: VKeyedCollection = {}; - for (const field of [ 'email', 'public_key' ]) { + for (const field of ["email", "public_key"]) { if (valuesToSet.hasOwnProperty(field)) { await Accounts.setField(req.vAuthToken, req.vAccount, field, valuesToSet.field, req.vAuthAccount, updates); - }; - }; - if (valuesToSet.hasOwnProperty('images')) { + } + } + if (valuesToSet.hasOwnProperty("images")) { if (valuesToSet.images.hero) { - await Accounts.setField(req.vAuthToken, req.vAccount, 'images_hero', valuesToSet.images.hero, req.vAuthAccount, updates); - }; + await Accounts.setField(req.vAuthToken, req.vAccount, "images_hero", valuesToSet.images.hero, req.vAuthAccount, updates); + } if (valuesToSet.images.tiny) { - await Accounts.setField(req.vAuthToken, req.vAccount, 'images_tiny', valuesToSet.images.tiny, req.vAuthAccount, updates); - }; + await Accounts.setField(req.vAuthToken, req.vAccount, "images_tiny", valuesToSet.images.tiny, req.vAuthAccount, updates); + } if (valuesToSet.images.thumbnail) { - await Accounts.setField(req.vAuthToken, req.vAccount, 'images_thumbnail', valuesToSet.images.thumbnail, req.vAuthAccount, updates); - }; - }; + await Accounts.setField(req.vAuthToken, req.vAccount, "images_thumbnail", valuesToSet.images.thumbnail, req.vAuthAccount, updates); + } + } await Accounts.updateEntityFields(req.vAuthAccount, updates); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Account not specified"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Account not specified'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; - }; + } next(); }; @@ -96,37 +90,40 @@ const procDeleteAccountId: RequestHandler = async (req: Request, resp: Response, if (Accounts.isAdmin(req.vAuthAccount)) { await Accounts.removeAccount(req.vAccount); await Accounts.removeAccountContext(req.vAccount); + } else { + req.vRestResp.respondFailure("Not an administrator"); } - else { - req.vRestResp.respondFailure('Not an administrator'); - }; + } else { + req.vRestResp.respondFailure("Target account does not exist"); } - else { - req.vRestResp.respondFailure('Target account does not exist'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/account/:accountId'; +export const name = "/api/v1/account/:accountId"; export const router = Router(); -router.get( '/api/v1/account/:accountId', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - procGetAccountId, - finishMetaverseAPI ] ); -router.post( '/api/v1/account/:accountId', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - procPostAccountId, - finishMetaverseAPI ] ); -router.delete('/api/v1/account/:accountId', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - procDeleteAccountId, - finishMetaverseAPI ] ); +router.get("/api/v1/account/:accountId", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + procGetAccountId, + finishMetaverseAPI +]); +router.post("/api/v1/account/:accountId", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + procPostAccountId, + finishMetaverseAPI +]); +router["delete"]("/api/v1/account/:accountId", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + procDeleteAccountId, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/account/accountId/field/fieldname.ts b/src/routes/api/v1/account/accountId/field/fieldname.ts index ba67e2cb..114fce1e 100755 --- a/src/routes/api/v1/account/accountId/field/fieldname.ts +++ b/src/routes/api/v1/account/accountId/field/fieldname.ts @@ -12,30 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI, tokenFromParams, accountFromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken, param1FromParams } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI, tokenFromParams, accountFromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken, param1FromParams } from "@Route-Tools/middleware"; -import { Accounts } from '@Entities/Accounts'; -import { VKeyedCollection } from '@Tools/vTypes'; +import { Accounts } from "@Entities/Accounts"; +import { VKeyedCollection } from "@Tools/vTypes"; // Get the scope of the logged in account const procGetField: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vAuthAccount) { - if (req.vAccount) { - req.vRestResp.Data = await Accounts.getField(req.vAuthToken, req.vAccount, req.vParam1, req.vAuthAccount); + if (req.vAuthAccount) { + if (req.vAccount) { + req.vRestResp.Data = await Accounts.getField(req.vAuthToken, req.vAccount, req.vParam1, req.vAuthAccount); + } else { + req.vRestResp.respondFailure("Target account not found"); + } + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure('Target account not found'); - }; - } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; - next(); + next(); }; // Add a role to my roles collection. @@ -43,48 +40,46 @@ const procGetField: RequestHandler = async (req: Request, resp: Response, next: const procPostField: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vAccount) { - if (req.body.hasOwnProperty('set')) { + if (req.body.hasOwnProperty("set")) { const updates: VKeyedCollection = {}; const success = await Accounts.setField(req.vAuthToken, req.vAccount, req.vParam1, req.body.set, req.vAuthAccount, updates); if (success.valid) { // Setting worked so update the database Accounts.updateEntityFields(req.vAccount, updates); + } else { + req.vRestResp.respondFailure("value could not be set:" + success.reason); } - else { - req.vRestResp.respondFailure('value could not be set:' + success.reason); - }; + } else { + req.vRestResp.respondFailure("no set value given"); } - else { - req.vRestResp.respondFailure('no set value given'); - }; + } else { + req.vRestResp.respondFailure("Target account not found"); } - else { - req.vRestResp.respondFailure('Target account not found'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/account/:accountId/field/:fieldname'; +export const name = "/api/v1/account/:accountId/field/:fieldname"; export const router = Router(); -router.get( '/api/v1/account/:accountId/field/:param1', - [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - param1FromParams, // req.vParam1 - procGetField, - finishMetaverseAPI - ] ); -router.post('/api/v1/account/:accountId/field/:param1', - [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - param1FromParams, // req.vParam1 - procPostField, - finishMetaverseAPI - ] ); +router.get("/api/v1/account/:accountId/field/:param1", + [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + param1FromParams, // req.vParam1 + procGetField, + finishMetaverseAPI + ]); +router.post("/api/v1/account/:accountId/field/:param1", + [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + param1FromParams, // req.vParam1 + procPostField, + finishMetaverseAPI + ]); diff --git a/src/routes/api/v1/account/accountId/tokens.ts b/src/routes/api/v1/account/accountId/tokens.ts index 63fa9ae1..18218a27 100755 --- a/src/routes/api/v1/account/accountId/tokens.ts +++ b/src/routes/api/v1/account/accountId/tokens.ts @@ -12,49 +12,50 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken, accountFromParams } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken, accountFromParams } from "@Route-Tools/middleware"; -import { AccountScopeFilter } from '@Entities/EntityFilters/AccountScopeFilter'; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { AccountScopeFilter } from "@Entities/EntityFilters/AccountScopeFilter"; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; -import { Logger } from '@Tools/Logging'; -import { Tokens } from '@Entities/Tokens'; -import { AuthToken } from '@Entities/AuthToken'; +import { Logger } from "@Tools/Logging"; +import { Tokens } from "@Entities/Tokens"; +import { AuthToken } from "@Entities/AuthToken"; // Return the tokens for the specified account. // One needs to be an admin in order to see other than one's own tokens const procGetAccountTokens: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vRestResp && req.vAuthAccount && req.vAccount) { const pager = new PaginationInfo(); - const scoper = new AccountScopeFilter(req.vAuthAccount, 'accountId'); + const scoper = new AccountScopeFilter(req.vAuthAccount, "accountId"); pager.parametersFromRequest(req); scoper.parametersFromRequest(req); - const acctFilter = new GenericFilter( { 'accountId': req.vAccount.id } ); + const acctFilter = new GenericFilter({ "accountId": req.vAccount.id }); const toks: AuthToken[] = []; for await (const tok of Tokens.enumerateAsync(acctFilter, pager, scoper)) { toks.push(tok); - }; + } req.vRestResp.Data = { - 'tokens': toks + "tokens": toks }; pager.addResponseFields(req); - }; + } next(); }; -export const name = '/api/v1/account/:accountId/tokens'; +export const name = "/api/v1/account/:accountId/tokens"; export const router = Router(); -router.get( '/api/v1/account/:accountId/tokens',[ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - procGetAccountTokens, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/account/:accountId/tokens", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + procGetAccountTokens, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/account/accountId/tokens/tokenId.ts b/src/routes/api/v1/account/accountId/tokens/tokenId.ts index def3fc00..ae22629e 100755 --- a/src/routes/api/v1/account/accountId/tokens/tokenId.ts +++ b/src/routes/api/v1/account/accountId/tokens/tokenId.ts @@ -12,18 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken, accountFromParams } from '@Route-Tools/middleware'; -import { tokenFromParams } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken, accountFromParams } from "@Route-Tools/middleware"; +import { tokenFromParams } from "@Route-Tools/middleware"; -import { Tokens } from '@Entities/Tokens'; -import { AccountScopeFilter } from '@Entities/EntityFilters/AccountScopeFilter'; +import { Tokens } from "@Entities/Tokens"; +import { AccountScopeFilter } from "@Entities/EntityFilters/AccountScopeFilter"; -import { Logger } from '@Tools/Logging'; -import { Accounts } from '@Entities/Accounts'; +import { Logger } from "@Tools/Logging"; +import { Accounts } from "@Entities/Accounts"; // metaverseServerApp.use(express.urlencoded({ extended: false })); @@ -34,50 +33,44 @@ const procDeleteToken: RequestHandler = async (req: Request, resp: Response, nex if (req.vAuthAccount) { if (req.vAccount) { if (req.vTokenId) { - const scoper = new AccountScopeFilter(req.vAuthAccount, 'accountId'); + const scoper = new AccountScopeFilter(req.vAuthAccount, "accountId"); scoper.parametersFromRequest(req); const tok = await Tokens.getTokenWithTokenId(req.vTokenId); if (tok) { - if ( scoper.AsAdmin() && Accounts.isAdmin(req.vAuthAccount) + if (scoper.AsAdmin() && Accounts.isAdmin(req.vAuthAccount) || req.vAuthAccount.id === tok.accountId) { if (req.vAccount.id === tok.accountId) { - await Tokens.removeToken(tok); + await Tokens.removeToken(tok); + } else { + req.vRestResp.respondFailure("Token account does not match requested account"); } - else { - req.vRestResp.respondFailure('Token account does not match requested account'); - }; + } else { + req.vRestResp.respondFailure("Unauthorized"); } - else { - req.vRestResp.respondFailure('Unauthorized'); - }; + } else { + req.vRestResp.respondFailure("Token not found"); } - else { - req.vRestResp.respondFailure('Token not found'); - }; + } else { + req.vRestResp.respondFailure("Token no speciied"); } - else { - req.vRestResp.respondFailure('Token no speciied'); - }; + } else { + req.vRestResp.respondFailure("Target acccount not found"); } - else { - req.vRestResp.respondFailure('Target acccount not found'); - }; + } else { + req.vRestResp.respondFailure("Not logged in"); } - else { - req.vRestResp.respondFailure('Not logged in'); - }; next(); }; -export const name = '/api/v1/account/:accoundId/tokens/:tokenId'; +export const name = "/api/v1/account/:accoundId/tokens/:tokenId"; export const router = Router(); -router.delete('/api/v1/account/:accountId/tokens/:tokenId', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - tokenFromParams, // req.vToken - procDeleteToken, - finishMetaverseAPI ] ); - - +router["delete"]("/api/v1/account/:accountId/tokens/:tokenId", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + tokenFromParams, // req.vToken + procDeleteToken, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/account/verify/email.ts b/src/routes/api/v1/account/verify/email.ts index 6a319257..5cba19d3 100644 --- a/src/routes/api/v1/account/verify/email.ts +++ b/src/routes/api/v1/account/verify/email.ts @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { Requests, RequestType } from '@Entities/Requests'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { Requests, RequestType } from "@Entities/Requests"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { HTTPStatusCode } from '@Route-Tools/RESTResponse'; -import { IsNotNullOrEmpty } from '@Tools/Misc'; -import { Config } from '@Base/config'; -import { logger, Logger } from '@Tools/Logging'; -import { AccountEntity } from '@Entities/AccountEntity'; +import { HTTPStatusCode } from "@Route-Tools/RESTResponse"; +import { IsNotNullOrEmpty } from "@Tools/Misc"; +import { Config } from "@Base/config"; +import { logger, Logger } from "@Tools/Logging"; +import { AccountEntity } from "@Entities/AccountEntity"; // metaverseServerApp.use(express.urlencoded({ extended: false })); @@ -32,12 +31,12 @@ import { AccountEntity } from '@Entities/AccountEntity'; // /api/v1/account/verify?a=ACCOUNT_ID&v=VERIFICATION_CODE const procGetVerifyAccount: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { logger.debug(`procGetVerifyAccount: `); - if (typeof(req.query.a) === 'string' && IsNotNullOrEmpty(req.query.a) - && typeof(req.query.v) === 'string' && IsNotNullOrEmpty(req.query.v)) { + if (typeof req.query.a === "string" && IsNotNullOrEmpty(req.query.a) + && typeof req.query.v === "string" && IsNotNullOrEmpty(req.query.v)) { const accountId = req.query.a; const verifyCode = req.query.v; logger.debug(`procGetVerifyAccount: acctId=${accountId}, code=${verifyCode}`); - const request = await Requests.getWithRequesterOrTarget(accountId, RequestType.VERIFYEMAIL) + const request = await Requests.getWithRequesterOrTarget(accountId, RequestType.VERIFYEMAIL); if (request) { if (request.verificationCode === verifyCode) { // A matching verification request @@ -50,37 +49,33 @@ const procGetVerifyAccount: RequestHandler = async (req: Request, resp: Response await Requests.remove(request); // If specified, send the user to a nice place - if (IsNotNullOrEmpty(Config['metaverse-server']['email-verification-success-redirect'])) { - const redirectURL = BuildRedirect(Config['metaverse-server']['email-verification-success-redirect'], - accountId); + if (IsNotNullOrEmpty(Config["metaverse-server"]["email-verification-success-redirect"])) { + const redirectURL = BuildRedirect(Config["metaverse-server"]["email-verification-success-redirect"], + accountId); Logger.debug(`procGetVerifyAccount: Redirect success to ${redirectURL}`); resp.statusCode = HTTPStatusCode.Found; - resp.setHeader('Location', redirectURL); - resp.setHeader('content-type', 'text/html'); + resp.setHeader("Location", redirectURL); + resp.setHeader("content-type", "text/html"); resp.send(); } - } - else { + } else { // Odd that we can't find the account. The pending verification request is no more. await Requests.remove(request); Logger.error(`procGetVerifyAccount: account ${accountId} could not be found for verification`); - RedirectFailure(resp, accountId, 'Not verified. Missing account'); - }; - } - else { + RedirectFailure(resp, accountId, "Not verified. Missing account"); + } + } else { Logger.error(`procGetVerifyAccount: verification code did not match for ${accountId}`); - RedirectFailure(resp, accountId, 'Not verified'); - }; - } - else { + RedirectFailure(resp, accountId, "Not verified"); + } + } else { Logger.error(`procGetVerifyAccount: attempt to verify account ${accountId} but no pending verification request`); - RedirectFailure(resp, accountId, 'Not verified. No pending verification request'); - }; - } - else { + RedirectFailure(resp, accountId, "Not verified. No pending verification request"); + } + } else { Logger.error(`procGetVerifyAccount: attempted verification with no parameters`); - RedirectFailure(resp, 'UNKNOWN', 'Verification parameters not present'); - }; + RedirectFailure(resp, "UNKNOWN", "Verification parameters not present"); + } // NOTE: no 'next()' operation because all paths return a redirect }; @@ -91,27 +86,29 @@ function BuildRedirect(pUrl: string, pAccountId: string, pReason?: string): stri if (IsNotNullOrEmpty(pReason)) { url = url.replace("FAILURE_REASON", encodeURI(pReason)); } - return url.replace("METAVERSE_SERVER_URL", Config.metaverse['metaverse-server-url']) - .replace("DASHBOARD_URL", Config.metaverse['dashboard-url']) + return url.replace("METAVERSE_SERVER_URL", Config.metaverse["metaverse-server-url"]) + .replace("DASHBOARD_URL", Config.metaverse["dashboard-url"]) .replace("ACCOUNT_ID", pAccountId); -}; +} // The operation failed so send the user to a sad place function RedirectFailure(pResp: Response, pAccountId: string, pReason: string): void { - const redirectURL = BuildRedirect(Config['metaverse-server']['email-verification-failure-redirect'], - pAccountId, pReason); + const redirectURL = BuildRedirect(Config["metaverse-server"]["email-verification-failure-redirect"], + pAccountId, pReason); Logger.debug(`procGetVerifyAccount: Redirect failure to ${redirectURL}`); pResp.statusCode = HTTPStatusCode.Found; - pResp.setHeader('Location', redirectURL); - pResp.setHeader('content-type', 'text/html'); + pResp.setHeader("Location", redirectURL); + pResp.setHeader("content-type", "text/html"); pResp.send(); -}; +} -export const name = '/api/v1/account/verify/email'; +export const name = "/api/v1/account/verify/email"; export const router = Router(); // Note that this is down one level -- if "/api/v1/account/verify" the "verify" is confused with account name -router.get( '/api/v1/account/verify/email', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - procGetVerifyAccount, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/account/verify/email", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + procGetVerifyAccount, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/accounts.ts b/src/routes/api/v1/accounts.ts index 828c8ea7..aa53e349 100755 --- a/src/routes/api/v1/accounts.ts +++ b/src/routes/api/v1/accounts.ts @@ -12,20 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { buildAccountInfo } from '@Route-Tools/Util'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { buildAccountInfo } from "@Route-Tools/Util"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { AccountScopeFilter } from '@Entities/EntityFilters/AccountScopeFilter'; -import { AccountFilterInfo } from '@Entities/EntityFilters/AccountFilterInfo'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { AccountScopeFilter } from "@Entities/EntityFilters/AccountScopeFilter"; +import { AccountFilterInfo } from "@Entities/EntityFilters/AccountFilterInfo"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; // Return account information // The accounts returned depend on the scope (whether admin) and the search criteria (infoer) @@ -42,7 +41,7 @@ const procGetAccounts: RequestHandler = async (req: Request, resp: Response, nex const accts: any[] = []; for await (const acct of Accounts.enumerateAsync(scoper, infoer, pager)) { accts.push(await buildAccountInfo(req, acct)); - }; + } req.vRestResp.Data = { accounts: accts @@ -50,18 +49,19 @@ const procGetAccounts: RequestHandler = async (req: Request, resp: Response, nex pager.addResponseFields(req); scoper.addResponseFields(req); infoer.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/accounts'; +export const name = "/api/v1/accounts"; export const router = Router(); -router.get( '/api/v1/accounts', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetAccounts, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/accounts", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetAccounts, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/commerce/available_updates.ts b/src/routes/api/v1/commerce/available_updates.ts index f4a826d2..00f9f988 100755 --- a/src/routes/api/v1/commerce/available_updates.ts +++ b/src/routes/api/v1/commerce/available_updates.ts @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; // GET /api/v1/commerce/available_updates?per_page=10 const procGetCommerceAvailableUpdates: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -26,10 +25,12 @@ const procGetCommerceAvailableUpdates: RequestHandler = async (req: Request, res next(); }; -export const name = '/api/v1/commerce/available_updates'; +export const name = "/api/v1/commerce/available_updates"; export const router = Router(); -router.get('/api/v1/commerce/available_updates', [ setupMetaverseAPI, - procGetCommerceAvailableUpdates, - finishMetaverseAPI ] ); +router.get("/api/v1/commerce/available_updates", [ + setupMetaverseAPI, + procGetCommerceAvailableUpdates, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/commerce/hfc_account.ts b/src/routes/api/v1/commerce/hfc_account.ts index 2eca2084..e07590d4 100755 --- a/src/routes/api/v1/commerce/hfc_account.ts +++ b/src/routes/api/v1/commerce/hfc_account.ts @@ -12,31 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; // PUT /api/v1/commerce/htc_account const procPutCommerceHfcAccount: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { - Logger.debug('procPutCommerceHfcAccount'); + Logger.debug("procPutCommerceHfcAccount"); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/commerce/hfc_account'; +export const name = "/api/v1/commerce/hfc_account"; export const router = Router(); -router.put('/api/v1/commerce/hfc_account', [ setupMetaverseAPI, - accountFromAuthToken, - procPutCommerceHfcAccount, - finishMetaverseAPI ] ); - +router.put("/api/v1/commerce/hfc_account", [ + setupMetaverseAPI, + accountFromAuthToken, + procPutCommerceHfcAccount, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/commerce/history.ts b/src/routes/api/v1/commerce/history.ts index 7516c742..3a1b6c59 100755 --- a/src/routes/api/v1/commerce/history.ts +++ b/src/routes/api/v1/commerce/history.ts @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; // GET /api/v1/commerce/history?per_page=10&since=1597015135.983 const procGetCommerceHistory: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -26,10 +25,12 @@ const procGetCommerceHistory: RequestHandler = async (req: Request, resp: Respon next(); }; -export const name = '/api/v1/commerce/history'; +export const name = "/api/v1/commerce/history"; export const router = Router(); -router.get('/api/v1/commerce/history', [ setupMetaverseAPI, - procGetCommerceHistory, - finishMetaverseAPI ] ); +router.get("/api/v1/commerce/history", [ + setupMetaverseAPI, + procGetCommerceHistory, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/commerce/marketplace_key.ts b/src/routes/api/v1/commerce/marketplace_key.ts index 41e41e5c..b1ca311a 100755 --- a/src/routes/api/v1/commerce/marketplace_key.ts +++ b/src/routes/api/v1/commerce/marketplace_key.ts @@ -12,26 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; // metaverseServerApp.use(express.urlencoded({ extended: false })); const procGetCommerceMarketplaceKey: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vRestResp) { req.vRestResp.Data = { - 'public_key': 'laksdjfioasdofuoavanvowieroiawueroaijsdlkfajsdlfjalsdkfjlasdjfaklhkoiu' + "public_key": "laksdjfioasdofuoavanvowieroiawueroaijsdlkfajsdlfjalsdkfjlasdjfaklhkoiu" }; } next(); }; -export const name = '/api/v1/commerce/marketplace_key'; +export const name = "/api/v1/commerce/marketplace_key"; export const router = Router(); -router.get('/api/v1/commerce/marketplace_key', [ setupMetaverseAPI, - procGetCommerceMarketplaceKey, - finishMetaverseAPI ] ); +router.get("/api/v1/commerce/marketplace_key", [ + setupMetaverseAPI, + procGetCommerceMarketplaceKey, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/domains.ts b/src/routes/api/v1/domains.ts index baaaf95f..69b1c90b 100755 --- a/src/routes/api/v1/domains.ts +++ b/src/routes/api/v1/domains.ts @@ -12,25 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Domains } from '@Entities/Domains'; -import { Places } from '@Entities/Places'; -import { DomainFields } from '@Entities/DomainFields'; -import { buildDomainInfoV1 } from '@Route-Tools/Util'; -import { buildDomainInfo, buildPlaceInfo } from '@Route-Tools/Util'; +import { Domains } from "@Entities/Domains"; +import { Places } from "@Entities/Places"; +import { DomainFields } from "@Entities/DomainFields"; +import { buildDomainInfoV1 } from "@Route-Tools/Util"; +import { buildDomainInfo, buildPlaceInfo } from "@Route-Tools/Util"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { AccountScopeFilter } from '@Entities/EntityFilters/AccountScopeFilter'; -import { VisibilityFilter } from '@Entities/EntityFilters/VisibilityFilter'; -import { HTTPStatusCode } from '@Route-Tools/RESTResponse'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { AccountScopeFilter } from "@Entities/EntityFilters/AccountScopeFilter"; +import { VisibilityFilter } from "@Entities/EntityFilters/VisibilityFilter"; +import { HTTPStatusCode } from "@Route-Tools/RESTResponse"; -import { GenUUID, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { GenUUID, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // GET /api/v1/domains const procGetDomains: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -48,25 +47,24 @@ const procGetDomains: RequestHandler = async (req: Request, resp: Response, next const domainArray: any[] = []; for await (const aDomain of Domains.enumerateAsync(scoper, pager)) { if (await visibilitier.criteriaTestAsync(req.vAuthAccount, aDomain)) { - domainArray.push( await buildDomainInfoV1(aDomain) ); + domainArray.push(await buildDomainInfoV1(aDomain)); } - }; + } req.vRestResp.Data = { - 'domains': domainArray + "domains": domainArray }; visibilitier.addResponseFields(req); scoper.addResponseFields(req); pager.addResponseFields(req); - } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); req.vRestResp.HTTPStatus = HTTPStatusCode.Unauthorized; - }; + } next(); }; - // Create a domain entry +// Create a domain entry const procPostDomains: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.body && req.body.domain && req.body.domain.label) { @@ -81,16 +79,16 @@ const procPostDomains: RequestHandler = async (req: Request, resp: Response, nex newDomain.apiKey = generatedAPIkey; if (req.vSenderKey) { newDomain.iPAddrOfFirstContact = req.vSenderKey; - }; + } if (req.body.domain.network_address) { newDomain.networkAddr = req.body.domain.network_address; - }; + } if (req.body.domain.network_port) { newDomain.networkPort = req.body.domain.network_port; - }; + } // Domain is associated with the account that is logged in for the creation - Logger.debug(`procPostDomains: associating account ${req.vAuthToken.accountId} with new domain ${newDomain.id}`) + Logger.debug(`procPostDomains: associating account ${req.vAuthToken.accountId} with new domain ${newDomain.id}`); newDomain.sponsorAccountId = req.vAuthToken.accountId; // Creating a domain also creates a Place for that domain @@ -99,10 +97,10 @@ const procPostDomains: RequestHandler = async (req: Request, resp: Response, nex const newPlace = await Places.createPlace(req.vAuthToken.accountId); newPlace.domainId = newDomain.id; newPlace.name = newPlacename; - newPlace.description = 'A place in ' + newDomain.name; + newPlace.description = "A place in " + newDomain.name; newPlace.maturity = newDomain.maturity; newPlace.iPAddrOfFirstContact = req.vSenderKey; - newPlace.managers = [ req.vAuthAccount.username ]; + newPlace.managers = [req.vAuthAccount.username]; // Now that the local structures are updated, store the new entries Domains.addDomain(newDomain); @@ -112,40 +110,40 @@ const procPostDomains: RequestHandler = async (req: Request, resp: Response, nex domainInfo.api_key = newDomain.apiKey; req.vRestResp.Data = { - 'domain': domainInfo, - 'place': await buildPlaceInfo(newPlace, newDomain) + "domain": domainInfo, + "place": await buildPlaceInfo(newPlace, newDomain) }; // some legacy requests want the domain information at the top level - req.vRestResp.addAdditionalField('domain', domainInfo); + req.vRestResp.addAdditionalField("domain", domainInfo); + } else { + req.vRestResp.respondFailure(ifValid.reason ?? "invalid domain name"); } - else { - req.vRestResp.respondFailure(ifValid.reason ?? 'invalid domain name'); - }; + } else { + req.vRestResp.respondFailure("label was empty"); } - else { - req.vRestResp.respondFailure('label was empty'); - }; + } else { + req.vRestResp.respondFailure("no label supplied"); } - else { - req.vRestResp.respondFailure('no label supplied'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/domains'; +export const name = "/api/v1/domains"; export const router = Router(); -router.get( '/api/v1/domains', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetDomains, - finishMetaverseAPI ] ); -router.post( '/api/v1/domains', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procPostDomains, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/domains", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetDomains, + finishMetaverseAPI +]); +router.post("/api/v1/domains", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procPostDomains, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/domains/domainId.ts b/src/routes/api/v1/domains/domainId.ts index 5bcfc270..8377bbf9 100755 --- a/src/routes/api/v1/domains/domainId.ts +++ b/src/routes/api/v1/domains/domainId.ts @@ -12,48 +12,46 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, domainAPIkeyFromBody, verifyDomainAccess } from '@Route-Tools/middleware'; -import { HTTPStatusCode } from '@Route-Tools/RESTResponse'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, domainAPIkeyFromBody, verifyDomainAccess } from "@Route-Tools/middleware"; +import { HTTPStatusCode } from "@Route-Tools/RESTResponse"; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { domainFromParams } from '@Route-Tools/middleware'; -import { Perm } from '@Route-Tools/Perm'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; -import { buildDomainInfoV1 } from '@Route-Tools/Util'; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { domainFromParams } from "@Route-Tools/middleware"; +import { Perm } from "@Route-Tools/Perm"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; +import { buildDomainInfoV1 } from "@Route-Tools/Util"; -import { Domains } from '@Entities/Domains'; -import { Accounts } from '@Entities/Accounts'; +import { Domains } from "@Entities/Domains"; +import { Accounts } from "@Entities/Accounts"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; -import Config from '@Base/config'; -import { IsNullOrEmpty } from '@Tools/Misc'; -import { updateObjectFields } from '@Tools/Db'; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; +import Config from "@Base/config"; +import { IsNullOrEmpty } from "@Tools/Misc"; +import { updateObjectFields } from "@Tools/Db"; // GET /api/v1/domains/:domainId // Return a small snippet if domain data for the domainId specified in the request const procGetDomainsDomainid: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vDomain) { - const aDomain = req.vDomain; - const domainInfo = await buildDomainInfoV1(aDomain); - // A few copies are added for compatibiliity with legacy code - domainInfo.id = domainInfo.domainId; + if (req.vDomain) { + const aDomain = req.vDomain; + const domainInfo = await buildDomainInfoV1(aDomain); + // A few copies are added for compatibiliity with legacy code + domainInfo.id = domainInfo.domainId; - req.vRestResp.Data = { - 'domain': domainInfo - }; - // For legacy code which expects the domain information at the top level - req.vRestResp.addAdditionalField('domain', domainInfo); - } - else { - req.vRestResp.respondFailure(req.vDomainError ?? 'Domain not found'); - // HTTP error will force domain renegotation - req.vRestResp.HTTPStatus = HTTPStatusCode.Unauthorized; - }; - next(); + req.vRestResp.Data = { + "domain": domainInfo + }; + // For legacy code which expects the domain information at the top level + req.vRestResp.addAdditionalField("domain", domainInfo); + } else { + req.vRestResp.respondFailure(req.vDomainError ?? "Domain not found"); + // HTTP error will force domain renegotation + req.vRestResp.HTTPStatus = HTTPStatusCode.Unauthorized; + } + next(); }; // PUT /api/v1/domains/:domainId @@ -62,34 +60,56 @@ const procGetDomainsDomainid: RequestHandler = async (req: Request, resp: Respon const procPutDomains: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vDomain) { // Either the domain itself or an admin can update the domain information - if (await checkAccessToEntity(req.vAuthToken, req.vDomain, [ Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN ])) { + if (await checkAccessToEntity(req.vAuthToken, req.vDomain, [Perm.DOMAIN, Perm.SPONSOR, Perm.ADMIN])) { const updated: VKeyedCollection = {}; let valuesToSet = req.body.domain; if (valuesToSet) { // 'valuesToSet' are the values sent to use in the request. // Collect the specific values set. Cannot just accept all because the // requestor could do things like set the password hash or other bad things. - for (const field of ['version', 'protocol', 'network_address', 'network_port', 'automatic_networking', - 'restricted', 'capacity', 'description', 'maturity', 'restriction', 'managers', 'tags' ]) { + for (const field of [ + "version", + "protocol", + "network_address", + "network_port", + "automatic_networking", + "restricted", + "capacity", + "description", + "maturity", + "restriction", + "managers", + "tags" + ]) { if (valuesToSet.hasOwnProperty(field)) { await Domains.setField(req.vAuthToken, req.vDomain, field, valuesToSet[field], req.vAuthAccount, updated); - }; - }; - if (valuesToSet.hasOwnProperty('heartbeat')) { - await Domains.setField(req.vAuthToken, req.vDomain, 'num_users', valuesToSet.heartbeat.num_users, req.vAuthAccount, updated); - await Domains.setField(req.vAuthToken, req.vDomain, 'num_anon_users', valuesToSet.heartbeat.num_anon_users, req.vAuthAccount, updated); - }; + } + } + if (valuesToSet.hasOwnProperty("heartbeat")) { + await Domains.setField(req.vAuthToken, req.vDomain, "num_users", valuesToSet.heartbeat.num_users, req.vAuthAccount, updated); + await Domains.setField(req.vAuthToken, req.vDomain, "num_anon_users", valuesToSet.heartbeat.num_anon_users, req.vAuthAccount, updated); + } if (valuesToSet.meta) { // Setting the domain specs with the domain settings pages returns 'meta' informtion valuesToSet = valuesToSet.meta; - for (const field of [ 'capacity', 'contact_info', 'description', 'managers', 'tags', 'images', - 'maturity', 'restriction', 'thumbnail', 'world_name' ]) { + for (const field of [ + "capacity", + "contact_info", + "description", + "managers", + "tags", + "images", + "maturity", + "restriction", + "thumbnail", + "world_name" + ]) { if (valuesToSet.hasOwnProperty(field)) { await Domains.setField(req.vAuthToken, req.vDomain, field, valuesToSet[field], req.vAuthAccount, updated); - }; - }; - }; + } + } + } /* FOLLOWING CODE DOES NOT WORK: the socket.remoteAddress is the IP addr of the front end proxy server. Need to use 'x-forwarded-for:' header @@ -111,22 +131,19 @@ const procPutDomains: RequestHandler = async (req: Request, resp: Response, next // This 'POST" is used as the domain heartbeat. Remember it's alive. updated.timeOfLastHeartbeat = new Date(); updated.active = true; - Logger.debug('procPutDomains. updating=' + JSON.stringify(updated)); + Logger.debug("procPutDomains. updating=" + JSON.stringify(updated)); Domains.updateEntityFields(req.vDomain, updated); + } else { + req.vRestResp.respondFailure("badly formed data"); } - else { - req.vRestResp.respondFailure('badly formed data'); - }; + } else { + req.vRestResp.respondFailure("Unauthorized"); } - else { - req.vRestResp.respondFailure('Unauthorized'); - }; - } - else { - req.vRestResp.respondFailure(req.vDomainError ?? 'Domain not found'); + } else { + req.vRestResp.respondFailure(req.vDomainError ?? "Domain not found"); // HTTP error will force domain renegotation req.vRestResp.HTTPStatus = HTTPStatusCode.Unauthorized; - }; + } next(); }; @@ -134,40 +151,43 @@ const procPutDomains: RequestHandler = async (req: Request, resp: Response, next const procDeleteDomains: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vDomain) { - if (await checkAccessToEntity(req.vAuthToken, req.vDomain, [ Perm.SPONSOR, Perm.ADMIN ])) { + if (await checkAccessToEntity(req.vAuthToken, req.vDomain, [Perm.SPONSOR, Perm.ADMIN])) { await Domains.removeDomain(req.vDomain); await Domains.removeDomainContext(req.vDomain); + } else { + req.vRestResp.respondFailure("Not authorized"); } - else { - req.vRestResp.respondFailure('Not authorized'); - }; + } else { + req.vRestResp.respondFailure(req.vDomainError ?? "Domain not found"); } - else { - req.vRestResp.respondFailure(req.vDomainError ?? 'Domain not found'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/domains/:domainId'; +export const name = "/api/v1/domains/:domainId"; export const router = Router(); -router.get( '/api/v1/domains/:domainId', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - domainFromParams, // req.vDomain - procGetDomainsDomainid, - finishMetaverseAPI ] ); -router.put( '/api/v1/domains/:domainId', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - domainFromParams, // req.vDomain - domainAPIkeyFromBody, // req.vDomainAPIKey - verifyDomainAccess, - procPutDomains, - finishMetaverseAPI ] ); -router.delete('/api/v1/domains/:domainId', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - domainFromParams, // req.vDomain - accountFromAuthToken, // req.vAuthAccount - procDeleteDomains, - finishMetaverseAPI ] ); +router.get("/api/v1/domains/:domainId", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + domainFromParams, // req.vDomain + procGetDomainsDomainid, + finishMetaverseAPI +]); +router.put("/api/v1/domains/:domainId", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + domainFromParams, // req.vDomain + domainAPIkeyFromBody, // req.vDomainAPIKey + verifyDomainAccess, + procPutDomains, + finishMetaverseAPI +]); +router["delete"]("/api/v1/domains/:domainId", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + domainFromParams, // req.vDomain + accountFromAuthToken, // req.vAuthAccount + procDeleteDomains, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/domains/domainId/field/fieldname.ts b/src/routes/api/v1/domains/domainId/field/fieldname.ts index bdf9db51..165e2363 100755 --- a/src/routes/api/v1/domains/domainId/field/fieldname.ts +++ b/src/routes/api/v1/domains/domainId/field/fieldname.ts @@ -12,28 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI, domainFromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken, param1FromParams } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI, domainFromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken, param1FromParams } from "@Route-Tools/middleware"; -import { Domains } from '@Entities/Domains'; -import { VKeyedCollection } from '@Tools/vTypes'; +import { Domains } from "@Entities/Domains"; +import { VKeyedCollection } from "@Tools/vTypes"; // Get the scope of the logged in account const procGetField: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vDomain) { req.vRestResp.Data = await Domains.getField(req.vAuthToken, req.vDomain, req.vParam1); + } else { + req.vRestResp.respondFailure(req.vDomainError ?? "Target domain not found"); } - else { - req.vRestResp.respondFailure(req.vDomainError ?? 'Target domain not found'); - }; - } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } next(); }; @@ -45,43 +42,41 @@ const procPostField: RequestHandler = async (req: Request, resp: Response, next: if (req.vDomain) { const updates: VKeyedCollection = {}; const success = await Domains.setField(req.vAuthToken, req.vDomain, req.vParam1, - req.body.set, req.vAuthAccount, updates); + req.body.set, req.vAuthAccount, updates); if (success.valid) { // Setting worked so update the database Domains.updateEntityFields(req.vDomain, updates); + } else { + req.vRestResp.respondFailure("value could not be set: " + success.reason); } - else { - req.vRestResp.respondFailure('value could not be set: ' + success.reason); - }; + } else { + req.vRestResp.respondFailure(req.vDomainError ?? "Target domain not found"); } - else { - req.vRestResp.respondFailure(req.vDomainError ?? 'Target domain not found'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/domains/:domainId/field/:fieldname'; +export const name = "/api/v1/domains/:domainId/field/:fieldname"; export const router = Router(); -router.get( '/api/v1/domains/:domainId/field/:param1', - [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - domainFromParams, // req.vDomain - param1FromParams, // req.vParam1 - procGetField, - finishMetaverseAPI - ] ); -router.post('/api/v1/domains/:domainId/field/:param1', - [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - domainFromParams, // req.vDomain - param1FromParams, // req.vParam1 - procPostField, - finishMetaverseAPI - ] ); - +router.get("/api/v1/domains/:domainId/field/:param1", + [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + domainFromParams, // req.vDomain + param1FromParams, // req.vParam1 + procGetField, + finishMetaverseAPI + ]); +router.post("/api/v1/domains/:domainId/field/:param1", + [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + domainFromParams, // req.vDomain + param1FromParams, // req.vParam1 + procPostField, + finishMetaverseAPI + ]); diff --git a/src/routes/api/v1/domains/domainId/ice_server_address.ts b/src/routes/api/v1/domains/domainId/ice_server_address.ts index 453f3d5a..98b7437f 100755 --- a/src/routes/api/v1/domains/domainId/ice_server_address.ts +++ b/src/routes/api/v1/domains/domainId/ice_server_address.ts @@ -12,39 +12,39 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { domainFromParams, domainAPIkeyFromBody, verifyDomainAccess } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { domainFromParams, domainAPIkeyFromBody, verifyDomainAccess } from "@Route-Tools/middleware"; -import { Logger } from '@Tools/Logging'; -import { Domains } from '@Entities/Domains'; +import { Logger } from "@Tools/Logging"; +import { Domains } from "@Entities/Domains"; // PUT /domains/:domainId/ice_server_address const procPutDomainsIceServerAddress: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - Logger.debug('procPutDomainsIceServerAddress'); + Logger.debug("procPutDomainsIceServerAddress"); if (req.vDomain) { if (req.body && req.body.domain && req.body.domain.ice_server_address) { const updates: any = { - 'iceServerAddr': req.body.domain.ice_server_address + "iceServerAddr": req.body.domain.ice_server_address }; Domains.updateEntityFields(req.vDomain, updates); - }; + } + } else { + req.vRestResp.respondFailure(req.vDomainError ?? "unauthorized"); } - else { - req.vRestResp.respondFailure(req.vDomainError ?? 'unauthorized'); - }; next(); }; -export const name = '/api/v1/domains/:domainId/ice_server_address'; +export const name = "/api/v1/domains/:domainId/ice_server_address"; export const router = Router(); -router.put( '/api/v1/domains/:domainId/ice_server_address', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - domainFromParams, // req.vDomain - domainAPIkeyFromBody, // req.vDomainAPIKey - verifyDomainAccess, - procPutDomainsIceServerAddress, - finishMetaverseAPI ] ); +router.put("/api/v1/domains/:domainId/ice_server_address", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + domainFromParams, // req.vDomain + domainAPIkeyFromBody, // req.vDomainAPIKey + verifyDomainAccess, + procPutDomainsIceServerAddress, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/domains/domainId/public_key.ts b/src/routes/api/v1/domains/domainId/public_key.ts index dbe61334..ef8c528d 100755 --- a/src/routes/api/v1/domains/domainId/public_key.ts +++ b/src/routes/api/v1/domains/domainId/public_key.ts @@ -12,35 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { Domains } from '@Entities/Domains'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { Domains } from "@Entities/Domains"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { domainFromParams, domainAPIkeyFromMultipart, verifyDomainAccess } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { domainFromParams, domainAPIkeyFromMultipart, verifyDomainAccess } from "@Route-Tools/middleware"; -import multer from 'multer'; +import multer from "multer"; -import { Logger } from '@Tools/Logging'; -import { createSimplifiedPublicKey, convertBinKeyToPEM } from '@Route-Tools/Util'; -import { HTTPStatusCode } from '@Route-Tools/RESTResponse'; +import { Logger } from "@Tools/Logging"; +import { createSimplifiedPublicKey, convertBinKeyToPEM } from "@Route-Tools/Util"; +import { HTTPStatusCode } from "@Route-Tools/RESTResponse"; // GET /domains/:domainId/public_key // For backward-compatibility, the PEM formatted public key is returned by this // request as a single line string without the BEGIN and END texts. const procGetDomainsPublicKey: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - Logger.debug('procGetDomainsPublicKey'); + Logger.debug("procGetDomainsPublicKey"); if (req.vDomain) { // Response is a simple public_key field req.vRestResp.Data = { - 'public_key': createSimplifiedPublicKey(req.vDomain.publicKey) + "public_key": createSimplifiedPublicKey(req.vDomain.publicKey) }; - } - else { + } else { req.vRestResp.HTTPStatus = HTTPStatusCode.Unauthorized; - req.vRestResp.respondFailure('No domain'); - }; + req.vRestResp.respondFailure("No domain"); + } next(); }; @@ -50,7 +48,7 @@ const procGetDomainsPublicKey: RequestHandler = async (req: Request, resp: Respo // To keep backward compatibility, we convert the PKCS1 key into a SPKI key in PEM format // ("PEM" format is "Privacy Enhanced Mail" format and has the "BEGIN" and "END" text included). const procPutDomainsPublicKey: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - Logger.debug('procPutDomainsPublicKey'); + Logger.debug("procPutDomainsPublicKey"); if (req.vDomain) { if (req.files) { try { @@ -58,46 +56,46 @@ const procPutDomainsPublicKey: RequestHandler = async (req: Request, resp: Respo const publicKeyBin: Buffer = (req.files as any).public_key[0].buffer; const fieldsToUpdate = { - 'publicKey': convertBinKeyToPEM(publicKeyBin) + "publicKey": convertBinKeyToPEM(publicKeyBin) }; await Domains.updateEntityFields(req.vDomain, fieldsToUpdate); + } catch (e) { + Logger.error("procPutDomainsPublicKey: exception converting: " + e); + req.vRestResp.respondFailure("exception converting public key"); } - catch (e) { - Logger.error('procPutDomainsPublicKey: exception converting: ' + e); - req.vRestResp.respondFailure('exception converting public key'); - } + } else { + Logger.error("procPutDomainsPublicKey: no files part of body"); + req.vRestResp.respondFailure("no public key supplied"); } - else { - Logger.error('procPutDomainsPublicKey: no files part of body'); - req.vRestResp.respondFailure('no public key supplied'); - }; - } - else { + } else { req.vRestResp.HTTPStatus = HTTPStatusCode.Unauthorized; - req.vRestResp.respondFailure(req.vDomainError ?? 'no such domain'); - }; + req.vRestResp.respondFailure(req.vDomainError ?? "no such domain"); + } next(); }; -export const name = '/api/v1/domains/public_key'; +export const name = "/api/v1/domains/public_key"; export const router = Router(); -router.get( '/api/v1/domains/:domainId/public_key', [ setupMetaverseAPI, - domainFromParams, - procGetDomainsPublicKey, - finishMetaverseAPI ] ); +router.get("/api/v1/domains/:domainId/public_key", [ + setupMetaverseAPI, + domainFromParams, + procGetDomainsPublicKey, + finishMetaverseAPI +]); // The public key is sent in binary in a multipart-form. // This creates an unpacker to catch fields 'api_key' and 'public_key' const multiStorage = multer.memoryStorage(); -const uploader = multer( { storage: multiStorage, }); -const multiParser = uploader.fields( [ { name: 'api_key' }, { name: 'public_key' }]); -router.put( '/api/v1/domains/:domainId/public_key', [ setupMetaverseAPI, - domainFromParams, // vRESTResp.vDomain - multiParser, // body['api_key'], files['public_key'].buffer - domainAPIkeyFromMultipart, // vRestRest.vDomainAPIKey - verifyDomainAccess, - procPutDomainsPublicKey, - finishMetaverseAPI ] ); - +const uploader = multer({ storage: multiStorage }); +const multiParser = uploader.fields([{ name: "api_key" }, { name: "public_key" }]); +router.put("/api/v1/domains/:domainId/public_key", [ + setupMetaverseAPI, + domainFromParams, // vRESTResp.vDomain + multiParser, // body['api_key'], files['public_key'].buffer + domainAPIkeyFromMultipart, // vRestRest.vDomainAPIKey + verifyDomainAccess, + procPutDomainsPublicKey, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/domains/temporary.ts b/src/routes/api/v1/domains/temporary.ts index fbcf2c5a..98093c2f 100755 --- a/src/routes/api/v1/domains/temporary.ts +++ b/src/routes/api/v1/domains/temporary.ts @@ -12,22 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, accountFromAuthToken, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, accountFromAuthToken, finishMetaverseAPI } from "@Route-Tools/middleware"; -import { Domains } from '@Entities/Domains'; -import { Places } from '@Entities/Places'; -import { buildDomainInfo, buildPlaceInfo } from '@Route-Tools/Util'; +import { Domains } from "@Entities/Domains"; +import { Places } from "@Entities/Places"; +import { buildDomainInfo, buildPlaceInfo } from "@Route-Tools/Util"; -import { uniqueNamesGenerator, adjectives, colors, animals } from 'unique-names-generator'; -import { Config as UniqueNamesConfig } from 'unique-names-generator'; +import { uniqueNamesGenerator, adjectives, colors, animals } from "unique-names-generator"; +import { Config as UniqueNamesConfig } from "unique-names-generator"; -import { GenUUID } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { GenUUID } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // metaverseServerApp.use(express.urlencoded({ extended: false })); @@ -37,11 +36,11 @@ import { Logger } from '@Tools/Logging'; // POST /domains/temporary const procPostDomainsTemporary: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (Config['metaverse-server']['allow-temp-domain-creation']) { + if (Config["metaverse-server"]["allow-temp-domain-creation"]) { const customConfig: UniqueNamesConfig = { // dictionaries: [ adjectives, colors, animals ], - dictionaries: [ colors, animals ], - separator: '-', + dictionaries: [colors, animals], + separator: "-", length: 2 }; const generatedPlacename: string = uniqueNamesGenerator(customConfig); @@ -52,20 +51,20 @@ const procPostDomainsTemporary: RequestHandler = async (req: Request, resp: Resp newDomain.apiKey = generatedAPIkey; if (req.vSenderKey) { newDomain.iPAddrOfFirstContact = req.vSenderKey; - }; + } // If the requestor is logged in, associate that account with the new domain/place if (req.vAuthToken) { - Logger.debug(`procPostDomainsTemporary: associating account ${req.vAuthToken.accountId} with new domain ${newDomain.id}`) + Logger.debug(`procPostDomainsTemporary: associating account ${req.vAuthToken.accountId} with new domain ${newDomain.id}`); newDomain.sponsorAccountId = req.vAuthToken.accountId; - }; + } // Creating a domain also creates a Place for that domain const newPlacename = await Places.uniqifyPlaceName(newDomain.name); const newPlace = await Places.createPlace(newDomain.sponsorAccountId); newPlace.domainId = newDomain.id; newPlace.name = newPlacename; - newPlace.description = 'A place in ' + newDomain.name; + newPlace.description = "A place in " + newDomain.name; newPlace.maturity = newDomain.maturity; newPlace.iPAddrOfFirstContact = req.vSenderKey; @@ -76,22 +75,23 @@ const procPostDomainsTemporary: RequestHandler = async (req: Request, resp: Resp Logger.info(`procPostDomainsTemporary: creating temporary domain "${newDomain.name}" and place "${newPlace.name}"`); req.vRestResp.Data = { - 'domain': await buildDomainInfo(newDomain), - 'place': await buildPlaceInfo(newPlace, newDomain) + "domain": await buildDomainInfo(newDomain), + "place": await buildPlaceInfo(newPlace, newDomain) }; req.vRestResp.Data.domain.api_key = newDomain.apiKey; - } - else { - req.vRestResp.respondFailure('domain temporary names not available'); + } else { + req.vRestResp.respondFailure("domain temporary names not available"); } next(); }; -export const name = '/api/v1/domains/temporary'; +export const name = "/api/v1/domains/temporary"; export const router = Router(); -router.post( '/api/v1/domains/temporary', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procPostDomainsTemporary, - finishMetaverseAPI ] ); \ No newline at end of file +router.post("/api/v1/domains/temporary", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procPostDomainsTemporary, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/places.ts b/src/routes/api/v1/places.ts index d293a105..9265c49c 100755 --- a/src/routes/api/v1/places.ts +++ b/src/routes/api/v1/places.ts @@ -12,29 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import Config from '@Base/config'; +import Config from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { buildPlaceInfo } from '@Route-Tools/Util'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { buildPlaceInfo } from "@Route-Tools/Util"; -import { Domains } from '@Entities/Domains'; -import { Places } from '@Entities/Places'; +import { Domains } from "@Entities/Domains"; +import { Places } from "@Entities/Places"; -import { Perm } from '@Route-Tools/Perm'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; +import { Perm } from "@Route-Tools/Perm"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { PlaceFilterInfo } from '@Entities/EntityFilters/PlaceFilterInfo'; -import { VisibilityFilter } from '@Entities/EntityFilters/VisibilityFilter'; -import { Maturity } from '@Entities/Sets/Maturity'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { PlaceFilterInfo } from "@Entities/EntityFilters/PlaceFilterInfo"; +import { VisibilityFilter } from "@Entities/EntityFilters/VisibilityFilter"; +import { Maturity } from "@Entities/Sets/Maturity"; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; -import { placeFields } from '@Entities/PlaceFields'; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; +import { placeFields } from "@Entities/PlaceFields"; // Return places information // As of 20210501, the Places information is public and the requestor does not need to @@ -42,7 +41,7 @@ import { placeFields } from '@Entities/PlaceFields'; // create the list of places to explore. This is also related to the fact that scripts // do not have authentication information for fetching. const procGetPlaces: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - // if (req.vAuthAccount) { + // if (req.vAuthAccount) { const pager = new PaginationInfo(); const placer = new PlaceFilterInfo(); // you can only see friends, connections, etc @@ -60,24 +59,24 @@ const procGetPlaces: RequestHandler = async (req: Request, resp: Response, next: if (aDomain && IsNotNullOrEmpty(aDomain.networkAddr)) { if (await visibilitier.criteriaTestAsync(place, aDomain)) { places.push(await buildPlaceInfo(place, aDomain)); - }; - }; - }; + } + } + } req.vRestResp.Data = { - 'places': places, - // Maturity catagories added so client knows what is defined in the metaverse-server - 'maturity-categories': Maturity.MaturityCategories + places, + // Maturity catagories added so client knows what is defined in the metaverse-server + "maturity-categories": Maturity.MaturityCategories }; visibilitier.addResponseFields(req); placer.addResponseFields(req); pager.addResponseFields(req); - // } - // else { - // req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - // }; - next(); + // } + // else { + // req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); + // }; + next(); }; // Create a Place. @@ -96,17 +95,16 @@ export const procPostPlaces: RequestHandler = async (req: Request, resp: Respons requestedDesc = req.body.place.description; requestedAddr = req.body.place.address; requestedDomainId = req.body.place.domainId; - } - else { + } else { requestedName = req.body.place_id; requestedAddr = req.body.path; requestedDomainId = req.body.domain_id; - }; + } if (requestedName && requestedAddr && requestedDomainId) { const aDomain = await Domains.getDomainWithId(requestedDomainId); if (aDomain) { - if (await checkAccessToEntity(req.vAuthToken, aDomain, [ Perm.SPONSOR, Perm.ADMIN ], req.vAuthAccount)) { + if (await checkAccessToEntity(req.vAuthToken, aDomain, [Perm.SPONSOR, Perm.ADMIN], req.vAuthAccount)) { const ifValid = await placeFields.name.validate(placeFields.name, req.vAuthAccount, requestedName); if (ifValid.valid) { const newPlace = await Places.createPlace(aDomain.sponsorAccountId); @@ -115,43 +113,42 @@ export const procPostPlaces: RequestHandler = async (req: Request, resp: Respons newPlace.path = requestedAddr; newPlace.domainId = aDomain.id; newPlace.maturity = aDomain.maturity ?? Maturity.UNRATED; - newPlace.managers = [ req.vAuthAccount.username ]; + newPlace.managers = [req.vAuthAccount.username]; Places.addPlace(newPlace); req.vRestResp.Data = buildPlaceInfo(newPlace, aDomain); + } else { + req.vRestResp.respondFailure(ifValid.reason ?? "place name already exists or is too long"); } - else { - req.vRestResp.respondFailure(ifValid.reason ?? 'place name already exists or is too long'); - }; + } else { + req.vRestResp.respondFailure("unauthorized"); } - else { - req.vRestResp.respondFailure('unauthorized'); - }; + } else { + req.vRestResp.respondFailure("name/address/domainId not specified"); } - else { - req.vRestResp.respondFailure('name/address/domainId not specified'); - }; + } else { + req.vRestResp.respondFailure("name, address, and domainId must be specified"); } - else { - req.vRestResp.respondFailure('name, address, and domainId must be specified'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/places'; +export const name = "/api/v1/places"; export const router = Router(); -router.get( '/api/v1/places', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetPlaces, - finishMetaverseAPI ] ); -router.post( '/api/v1/places', - [ setupMetaverseAPI, // req.vRESTResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procPostPlaces, - finishMetaverseAPI ] ); +router.get("/api/v1/places", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetPlaces, + finishMetaverseAPI +]); +router.post("/api/v1/places", + [ + setupMetaverseAPI, // req.vRESTResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procPostPlaces, + finishMetaverseAPI + ]); diff --git a/src/routes/api/v1/places/current.ts b/src/routes/api/v1/places/current.ts index 717492a5..1602b67f 100644 --- a/src/routes/api/v1/places/current.ts +++ b/src/routes/api/v1/places/current.ts @@ -12,28 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import Config from '@Base/config'; +import Config from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams, placeFromParams } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams, placeFromParams } from "@Route-Tools/middleware"; -import { Tokens } from '@Entities/Tokens'; +import { Tokens } from "@Entities/Tokens"; -import { Places } from '@Entities/Places'; +import { Places } from "@Entities/Places"; -import { IsNotNullOrEmpty, IsNullOrEmpty } from '@Tools/Misc'; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { IsNotNullOrEmpty, IsNullOrEmpty } from "@Tools/Misc"; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; interface CurrentBody { - placeId: string, - current_api_key: string, - current_attendance: number, - current_images: string[], - current_info: string -}; + placeId: string, + current_api_key: string, + current_attendance: number, + current_images: string[], + current_info: string +} export const procPostPlaceCurrent: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { const reqBody = req.body as CurrentBody; @@ -51,53 +50,46 @@ export const procPostPlaceCurrent: RequestHandler = async (req: Request, resp: R // the authentication for setting the fields with the special Admin token const specialAuth = Tokens.createSpecialAdminToken(); - for (const field of [ 'current_attendance', 'current_images', 'current_info']) { + for (const field of ["current_attendance", "current_images", "current_info"]) { // If the requestor is setting a value, make sure it's legal before updating if (reqBody.hasOwnProperty(field)) { const validity = await Places.validateFieldValue(field, req.body[field]); if (validity.valid) { await Places.setField(specialAuth, aPlace, field, req.body[field], undefined, updates); - } - else { + } else { req.vRestResp.respondFailure(`Value for ${field} is not valid: ${validity.reason}`); - }; - }; - }; + } + } + } // Update the database with the new values updates.currentLastUpdateTime = new Date(); await Places.updateEntityFields(aPlace, updates); + } else { + req.vRestResp.respondFailure("current_api_key does not match Places key"); } - else { - req.vRestResp.respondFailure('current_api_key does not match Places key'); - }; + } else { + req.vRestResp.respondFailure("Place apikey lookup failed"); } - else { - req.vRestResp.respondFailure('Place apikey lookup failed'); - }; + } else { + req.vRestResp.respondFailure("Place specified by placeId does not exist"); } - else { - req.vRestResp.respondFailure('Place specified by placeId does not exist'); - }; + } else { + req.vRestResp.respondFailure("No current_api_key"); } - else { - req.vRestResp.respondFailure('No current_api_key'); - }; + } else { + req.vRestResp.respondFailure("No placeId"); } - else { - req.vRestResp.respondFailure('No placeId'); - }; next(); }; -export const name = '/api/v1/places/current'; +export const name = "/api/v1/places/current"; export const router = Router(); -router.post( '/api/v1/places/current', - [ setupMetaverseAPI, // req.vRESTResp - procPostPlaceCurrent, - finishMetaverseAPI ] ); - - - +router.post("/api/v1/places/current", + [ + setupMetaverseAPI, // req.vRESTResp + procPostPlaceCurrent, + finishMetaverseAPI + ]); diff --git a/src/routes/api/v1/places/placeId.ts b/src/routes/api/v1/places/placeId.ts index c8b82266..6f8923f9 100755 --- a/src/routes/api/v1/places/placeId.ts +++ b/src/routes/api/v1/places/placeId.ts @@ -12,43 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import Config from '@Base/config'; +import Config from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams, placeFromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams, placeFromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Perm } from '@Route-Tools/Perm'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; +import { Perm } from "@Route-Tools/Perm"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; -import { buildPlaceInfo } from '@Route-Tools/Util'; +import { buildPlaceInfo } from "@Route-Tools/Util"; -import { Accounts } from '@Entities/Accounts'; -import { Places } from '@Entities/Places'; -import { Domains } from '@Entities/Domains'; -import { Maturity } from '@Entities/Sets/Maturity'; +import { Accounts } from "@Entities/Accounts"; +import { Places } from "@Entities/Places"; +import { Domains } from "@Entities/Domains"; +import { Maturity } from "@Entities/Sets/Maturity"; -import { IsNotNullOrEmpty, IsNullOrEmpty } from '@Tools/Misc'; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { IsNotNullOrEmpty, IsNullOrEmpty } from "@Tools/Misc"; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; export const procGetPlacesPlaceId: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vPlace) { const placeInfo = await buildPlaceInfo(req.vPlace); // If the requestor is the 'owner' of the place, add APIKey to the response - if (await checkAccessToEntity(req.vAuthToken, req.vPlace, [ Perm.DOMAINACCESS, Perm.ADMIN ], req.vAuthAccount)) { + if (await checkAccessToEntity(req.vAuthToken, req.vPlace, [Perm.DOMAINACCESS, Perm.ADMIN], req.vAuthAccount)) { placeInfo.current_api_key = await Places.getCurrentInfoAPIKey(req.vPlace); - }; + } req.vRestResp.Data = { - 'place': placeInfo, - 'maturity-categories': Maturity.MaturityCategories + "place": placeInfo, + "maturity-categories": Maturity.MaturityCategories }; + } else { + req.vRestResp.respondFailure("No such place"); } - else { - req.vRestResp.respondFailure('No such place'); - }; next(); }; @@ -57,84 +55,81 @@ export const procGetPlacesPlaceId: RequestHandler = async (req: Request, resp: R export const procPutPlacesPlaceId: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vPlace) { - if (await checkAccessToEntity(req.vAuthToken, req.vPlace, [ Perm.DOMAINACCESS, Perm.ADMIN ], req.vAuthAccount)) { + if (await checkAccessToEntity(req.vAuthToken, req.vPlace, [Perm.DOMAINACCESS, Perm.ADMIN], req.vAuthAccount)) { if (req.body.place) { const updates: VKeyedCollection = {}; if (req.body.place.pointee_query) { // The caller specified a domain. Either the same domain or changing if (req.body.place.pointee_query !== req.vPlace.domainId) { - Logger.info(`procPutPlacesPlaceId: domain changing from ${req.vPlace.domainId} to ${req.body.place.pointee_query}`) + Logger.info(`procPutPlacesPlaceId: domain changing from ${req.vPlace.domainId} to ${req.body.place.pointee_query}`); req.vPlace.domainId = req.body.place.pointee_query; updates.domainId = req.vPlace.domainId; - }; - }; - for (const field of [ 'path', 'description', 'thumbnail' ]) { + } + } + for (const field of ["path", "description", "thumbnail"]) { if (req.body.place.hasOwnProperty(field)) { await Places.setField(req.vAuthToken, req.vPlace, field, req.body.place[field], req.vAuthAccount, updates); - }; - }; + } + } Places.updateEntityFields(req.vPlace, updates); + } else { + req.vRestResp.respondFailure("badly formed data"); } - else { - req.vRestResp.respondFailure('badly formed data'); - }; + } else { + req.vRestResp.respondFailure("unauthorized"); } - else { - req.vRestResp.respondFailure('unauthorized'); - }; + } else { + req.vRestResp.respondFailure("Target place not found"); } - else { - req.vRestResp.respondFailure('Target place not found'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; // Delete a Place export const procDeletePlacesPlaceId: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vPlace) { - if (await checkAccessToEntity(req.vAuthToken, req.vPlace, [ Perm.DOMAINACCESS, Perm.ADMIN ], req.vAuthAccount)) { - Logger.info(`procDeletePlacesPlaceId: deleting place "${req.vPlace.name}", id=${req.vPlace.id}`); - await Places.removePlace(req.vPlace); + if (await checkAccessToEntity(req.vAuthToken, req.vPlace, [Perm.DOMAINACCESS, Perm.ADMIN], req.vAuthAccount)) { + Logger.info(`procDeletePlacesPlaceId: deleting place "${req.vPlace.name}", id=${req.vPlace.id}`); + await Places.removePlace(req.vPlace); + } else { + req.vRestResp.respondFailure("unauthorized"); } - else { - req.vRestResp.respondFailure('unauthorized'); - }; + } else { + req.vRestResp.respondFailure("Target place not found"); } - else { - req.vRestResp.respondFailure('Target place not found'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/places/:placeId'; +export const name = "/api/v1/places/:placeId"; export const router = Router(); -router.get( '/api/v1/places/:placeId', - [ setupMetaverseAPI, // req.vRESTResp - accountFromAuthToken, // req.vAuthAccount - placeFromParams, // req.vPlace - procGetPlacesPlaceId, - finishMetaverseAPI ] ); -router.put( '/api/v1/places/:placeId', - [ setupMetaverseAPI, // req.vRESTResp - accountFromAuthToken, // req.vAuthAccount - placeFromParams, // req.vPlace - procPutPlacesPlaceId, - finishMetaverseAPI ] ); -router.delete( '/api/v1/places/:placeId', - [ setupMetaverseAPI, // req.vRESTResp - accountFromAuthToken, // req.vAuthAccount - placeFromParams, // req.vPlace - procDeletePlacesPlaceId, - finishMetaverseAPI ] ); - - +router.get("/api/v1/places/:placeId", + [ + setupMetaverseAPI, // req.vRESTResp + accountFromAuthToken, // req.vAuthAccount + placeFromParams, // req.vPlace + procGetPlacesPlaceId, + finishMetaverseAPI + ]); +router.put("/api/v1/places/:placeId", + [ + setupMetaverseAPI, // req.vRESTResp + accountFromAuthToken, // req.vAuthAccount + placeFromParams, // req.vPlace + procPutPlacesPlaceId, + finishMetaverseAPI + ]); +router["delete"]("/api/v1/places/:placeId", + [ + setupMetaverseAPI, // req.vRESTResp + accountFromAuthToken, // req.vAuthAccount + placeFromParams, // req.vPlace + procDeletePlacesPlaceId, + finishMetaverseAPI + ]); diff --git a/src/routes/api/v1/places/placeId/field/fieldname.ts b/src/routes/api/v1/places/placeId/field/fieldname.ts index 713fbe76..6731d76b 100755 --- a/src/routes/api/v1/places/placeId/field/fieldname.ts +++ b/src/routes/api/v1/places/placeId/field/fieldname.ts @@ -12,76 +12,71 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI, tokenFromParams, accountFromParams, placeFromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken, param1FromParams, param2FromParams } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI, tokenFromParams, accountFromParams, placeFromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken, param1FromParams, param2FromParams } from "@Route-Tools/middleware"; -import { Places } from '@Entities/Places'; +import { Places } from "@Entities/Places"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { IsNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { VKeyedCollection } from "@Tools/vTypes"; +import { IsNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // Get a field from a place entry const procGetField: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vPlace && req.vParam1) { - req.vRestResp.Data = await Places.getField(req.vAuthToken, req.vPlace, req.vParam1, req.vAuthAccount); - } - else { - req.vRestResp.respondFailure('no such place'); - }; - next(); + if (req.vPlace && req.vParam1) { + req.vRestResp.Data = await Places.getField(req.vAuthToken, req.vPlace, req.vParam1, req.vAuthAccount); + } else { + req.vRestResp.respondFailure("no such place"); + } + next(); }; // Change a field in a place const procPostField: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vPlace && req.vParam1) { - if (req.body.hasOwnProperty('set')) { + if (req.body.hasOwnProperty("set")) { const updates: VKeyedCollection = {}; const success = await Places.setField(req.vAuthToken, req.vPlace, req.vParam1, req.body.set, req.vAuthAccount, updates); if (success.valid) { // Setting worked so update the database Places.updateEntityFields(req.vPlace, updates); + } else { + req.vRestResp.respondFailure("value could not be set: " + success.reason); } - else { - req.vRestResp.respondFailure('value could not be set: ' + success.reason); - }; + } else { + req.vRestResp.respondFailure("no set value given"); } - else { - req.vRestResp.respondFailure('no set value given'); - }; + } else { + req.vRestResp.respondFailure("no such place"); } - else { - req.vRestResp.respondFailure('no such place'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/places/:placeId/field/:fieldname'; +export const name = "/api/v1/places/:placeId/field/:fieldname"; export const router = Router(); -router.get( '/api/v1/places/:placeId/field/:param1', - [ setupMetaverseAPI, // req.vRestResp - placeFromParams, // req.vPlace - param1FromParams, // req.vParam1 (field name) - procGetField, - finishMetaverseAPI - ] ); -router.post('/api/v1/places/:placeId/field/:param1', - [ setupMetaverseAPI, // req.vRestResp - accountFromAuthToken, // req.vAuthAccount - placeFromParams, // req.vPlace - param1FromParams, // req.vParam1 (field name) - procPostField, - finishMetaverseAPI - ] ); - +router.get("/api/v1/places/:placeId/field/:param1", + [ + setupMetaverseAPI, // req.vRestResp + placeFromParams, // req.vPlace + param1FromParams, // req.vParam1 (field name) + procGetField, + finishMetaverseAPI + ]); +router.post("/api/v1/places/:placeId/field/:param1", + [ + setupMetaverseAPI, // req.vRestResp + accountFromAuthToken, // req.vAuthAccount + placeFromParams, // req.vPlace + param1FromParams, // req.vParam1 (field name) + procPostField, + finishMetaverseAPI + ]); diff --git a/src/routes/api/v1/profile/accountId.ts b/src/routes/api/v1/profile/accountId.ts index b452f086..c699387c 100644 --- a/src/routes/api/v1/profile/accountId.ts +++ b/src/routes/api/v1/profile/accountId.ts @@ -12,46 +12,45 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken, accountFromParams } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken, accountFromParams } from "@Route-Tools/middleware"; -import { Perm } from '@Route-Tools/Perm'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; -import { buildAccountProfile } from '@Route-Tools/Util'; +import { Perm } from "@Route-Tools/Perm"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; +import { buildAccountProfile } from "@Route-Tools/Util"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; // metaverseServerApp.use(express.urlencoded({ extended: false })); const procGetProfileAccountId: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAccount) { - if (await checkAccessToEntity(req.vAuthToken, req.vAccount, [ Perm.PUBLIC, Perm.OWNER, Perm.ADMIN ])) { + if (await checkAccessToEntity(req.vAuthToken, req.vAccount, [Perm.PUBLIC, Perm.OWNER, Perm.ADMIN])) { req.vRestResp.Data = { account: await buildAccountProfile(req, req.vAccount) }; + } else { + req.vRestResp.respondFailure("Unauthorized"); } - else { - req.vRestResp.respondFailure('Unauthorized'); - }; + } else { + req.vRestResp.respondFailure("Target account not found"); } - else { - req.vRestResp.respondFailure('Target account not found'); - }; next(); }; -export const name = '/api/v1/profile/:accountId'; +export const name = "/api/v1/profile/:accountId"; export const router = Router(); -router.get( '/api/v1/profile/:accountId', [ setupMetaverseAPI, // req.vRestResp - accountFromParams, // req.vAccount - procGetProfileAccountId, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/profile/:accountId", [ + setupMetaverseAPI, // req.vRestResp + accountFromParams, // req.vAccount + procGetProfileAccountId, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/profiles.ts b/src/routes/api/v1/profiles.ts index 736bf033..8a3cb22e 100644 --- a/src/routes/api/v1/profiles.ts +++ b/src/routes/api/v1/profiles.ts @@ -12,36 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { buildAccountProfile } from '@Route-Tools/Util'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { buildAccountProfile } from "@Route-Tools/Util"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { Availability } from '@Entities/Sets/Availability'; +import { Availability } from "@Entities/Sets/Availability"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; -import { Logger } from '@Tools/Logging'; -import { GenericFilter } from '@Entities/EntityFilters/GenericFilter'; +import { Logger } from "@Tools/Logging"; +import { GenericFilter } from "@Entities/EntityFilters/GenericFilter"; // Return account information // The accounts returned depend on the scope (whether admin) and the search criteria (infoer) const procGetProfiles: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { const pager = new PaginationInfo(); // Can only see accounts that have unspecified availability or "all" - const scoper = new GenericFilter( { "$or": [ { "availability": { "$exists": false }}, - { "availability": Availability.ALL }, - ] } ); + const scoper = new GenericFilter({ "$or": [ + { "availability": { "$exists": false } }, + { "availability": Availability.ALL } + ] }); pager.parametersFromRequest(req); // Loop through all the filtered accounts and create array of info const accts: any[] = []; for await (const acct of Accounts.enumerateAsync(scoper, pager)) { accts.push(await buildAccountProfile(req, acct)); - }; + } req.vRestResp.Data = { profiles: accts @@ -53,10 +53,12 @@ const procGetProfiles: RequestHandler = async (req: Request, resp: Response, nex next(); }; -export const name = '/api/v1/profiles'; +export const name = "/api/v1/profiles"; export const router = Router(); -router.get( '/api/v1/profiles', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - procGetProfiles, - finishMetaverseAPI ] ); +router.get("/api/v1/profiles", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + procGetProfiles, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/requests.ts b/src/routes/api/v1/requests.ts index 8213b696..bf417350 100755 --- a/src/routes/api/v1/requests.ts +++ b/src/routes/api/v1/requests.ts @@ -12,17 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Requests, RequestType } from '@Entities/Requests'; +import { Requests, RequestType } from "@Entities/Requests"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { RequestScopeFilter } from '@Entities/EntityFilters/RequestScopeFilter'; -import { Logger } from '@Tools/Logging'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { RequestScopeFilter } from "@Entities/EntityFilters/RequestScopeFilter"; +import { Logger } from "@Tools/Logging"; const procGetRequests: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { @@ -35,26 +34,26 @@ const procGetRequests: RequestHandler = async (req: Request, resp: Response, nex const reqs: any[] = []; for await (const aReq of Requests.enumerateAsync(scoper, pager)) { const thisReq: any = { - 'id': aReq.id, - 'type': aReq.requestType, - 'requesting_account_id': aReq.requestingAccountId, - 'target_account_id': aReq.targetAccountId, - 'when_created': aReq.whenCreated?.toISOString(), - 'when_created_s': aReq.whenCreated?.getTime().toString(), - 'expiration_time': aReq.expirationTime?.toISOString(), - 'expiration_time_s': aReq.expirationTime?.getTime().toString() + "id": aReq.id, + "type": aReq.requestType, + "requesting_account_id": aReq.requestingAccountId, + "target_account_id": aReq.targetAccountId, + "when_created": aReq.whenCreated?.toISOString(), + "when_created_s": aReq.whenCreated?.getTime().toString(), + "expiration_time": aReq.expirationTime?.toISOString(), + "expiration_time_s": aReq.expirationTime?.getTime().toString() }; switch (aReq.requestType) { case RequestType.HANDSHAKE: thisReq.handshake = { - 'requester_id': aReq.requesterNodeId, - 'target_id': aReq.targetNodeId, - 'requester_accepted': aReq.requesterAccepted, - 'target_accepted': aReq.targetAccepted + "requester_id": aReq.requesterNodeId, + "target_id": aReq.targetNodeId, + "requester_accepted": aReq.requesterAccepted, + "target_accepted": aReq.targetAccepted }; - }; + } reqs.push(thisReq); - }; + } req.vRestResp.Data = { requests: reqs @@ -62,18 +61,19 @@ const procGetRequests: RequestHandler = async (req: Request, resp: Response, nex scoper.addResponseFields(req); pager.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/requests'; +export const name = "/api/v1/requests"; export const router = Router(); -router.get( '/api/v1/requests', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetRequests, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/requests", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetRequests, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/stats/category/category.ts b/src/routes/api/v1/stats/category/category.ts index ba195304..0fdd95fd 100755 --- a/src/routes/api/v1/stats/category/category.ts +++ b/src/routes/api/v1/stats/category/category.ts @@ -12,54 +12,52 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import os from 'os'; +import os from "os"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Monitoring } from '@Monitoring/Monitoring'; +import { Monitoring } from "@Monitoring/Monitoring"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; const procGetCategoryStats: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vParam1) { let includeHistory = true; if (req.query.history) { - if (typeof(req.query.history) === 'string') { - includeHistory = [ 'false', 'no' ].includes(req.query.history) ? false : true; - }; - }; + if (typeof req.query.history === "string") { + includeHistory = !["false", "no"].includes(req.query.history); + } + } - if (['os', 'server', 'metaverse'].includes(req.vParam1)) { - const data:any = {}; + if (["os", "server", "metaverse"].includes(req.vParam1)) { + const data: any = {}; data[req.vParam1] = Monitoring.getStat(req.vParam1)?.Report(includeHistory); req.vRestResp.Data = data; + } else { + req.vRestResp.respondFailure("unknown stat"); } - else { - req.vRestResp.respondFailure('unknown stat'); - }; + } else { + req.vRestResp.respondFailure("category not specified"); } - else { - req.vRestResp.respondFailure('category not specified'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/stats/category/:category'; +export const name = "/api/v1/stats/category/:category"; export const router = Router(); -router.get('/api/v1/stats/category/:param1', [ setupMetaverseAPI, // req.vRestResp - accountFromAuthToken, // req.vAuthAccount - param1FromParams, // req.vParam1 - procGetCategoryStats, - finishMetaverseAPI ] ); +router.get("/api/v1/stats/category/:param1", [ + setupMetaverseAPI, // req.vRestResp + accountFromAuthToken, // req.vAuthAccount + param1FromParams, // req.vParam1 + procGetCategoryStats, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/stats/list.ts b/src/routes/api/v1/stats/list.ts index 16c32ef9..1b01625d 100755 --- a/src/routes/api/v1/stats/list.ts +++ b/src/routes/api/v1/stats/list.ts @@ -12,43 +12,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import os from 'os'; +import os from "os"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Logger } from '@Tools/Logging'; -import { Monitoring } from '@Monitoring/Monitoring'; +import { Logger } from "@Tools/Logging"; +import { Monitoring } from "@Monitoring/Monitoring"; const procGetStatList: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { const statInfo: any[] = []; for (const stat of Monitoring.getStats()) { statInfo.push({ - 'name': stat.name, - 'category': stat.category, - 'unit': stat.unit + "name": stat.name, + "category": stat.category, + "unit": stat.unit }); - }; + } req.vRestResp.Data = { - 'stats': statInfo + "stats": statInfo }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/stats/list'; +export const name = "/api/v1/stats/list"; export const router = Router(); -router.get('/api/v1/stats/list', [ setupMetaverseAPI, // req.vRestResp - accountFromAuthToken, // req.vAuthAccount - procGetStatList, - finishMetaverseAPI ] ); - +router.get("/api/v1/stats/list", [ + setupMetaverseAPI, // req.vRestResp + accountFromAuthToken, // req.vAuthAccount + procGetStatList, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/stats/stat.ts b/src/routes/api/v1/stats/stat.ts index cf750cb1..8dcadcda 100755 --- a/src/routes/api/v1/stats/stat.ts +++ b/src/routes/api/v1/stats/stat.ts @@ -12,55 +12,51 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import os from 'os'; +import os from "os"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Monitoring } from '@Monitoring/Monitoring'; +import { Monitoring } from "@Monitoring/Monitoring"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; const procGetStat: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (req.vParam1) { let includeHistory = true; if (req.query.history) { - if (typeof(req.query.history) === 'string') { - includeHistory = [ 'false', 'no' ].includes(req.query.history) ? false : true; - }; - }; + if (typeof req.query.history === "string") { + includeHistory = !["false", "no"].includes(req.query.history); + } + } const stat = Monitoring.getStat(req.vParam1); if (stat) { req.vRestResp.Data = { - 'stat': stat.Report(includeHistory) + "stat": stat.Report(includeHistory) }; + } else { + req.vRestResp.respondFailure("Unknown stat"); } - else { - req.vRestResp.respondFailure('Unknown stat'); - }; + } else { + req.vRestResp.respondFailure("missing parameter"); } - else { - req.vRestResp.respondFailure('missing parameter'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/stats/stat/:name'; +export const name = "/api/v1/stats/stat/:name"; export const router = Router(); -router.get('/api/v1/stats/stat/:param1', [ setupMetaverseAPI, // req.vRestResp - accountFromAuthToken, // req.vAuthAccount - param1FromParams, // req.vParam1 - procGetStat, - finishMetaverseAPI ] ); - - +router.get("/api/v1/stats/stat/:param1", [ + setupMetaverseAPI, // req.vRestResp + accountFromAuthToken, // req.vAuthAccount + param1FromParams, // req.vParam1 + procGetStat, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/token/new.ts b/src/routes/api/v1/token/new.ts index 998d0ece..741bd324 100755 --- a/src/routes/api/v1/token/new.ts +++ b/src/routes/api/v1/token/new.ts @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Tokens, TokenScope } from '@Entities/Tokens'; -import { IsNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { Tokens, TokenScope } from "@Entities/Tokens"; +import { IsNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // Request that creates a token for the passed account. // Query parameter of 'scope' can say wether token is for 'owner' or 'domain'. @@ -28,38 +27,35 @@ const procPostTokenNew: RequestHandler = async (req: Request, resp: Response, ne if (req.vAuthAccount) { // The user passes the scope but make sure we know it's one we know let scope = TokenScope.OWNER; - if (req.query && req.query.scope && typeof(req.query.scope) === 'string') { - scope = req.query.scope as string; - }; + if (req.query && req.query.scope && typeof req.query.scope === "string") { + scope = req.query.scope; + } if (TokenScope.KnownScope(scope)) { - const tokenInfo = await Tokens.createToken(req.vAuthAccount.id, [ scope ]); + const tokenInfo = await Tokens.createToken(req.vAuthAccount.id, [scope]); await Tokens.addToken(tokenInfo); req.vRestResp.Data = { - 'token': tokenInfo.token, - 'token_id': tokenInfo.id, - 'refresh_token': tokenInfo.refreshToken, - 'token_expiration_seconds': (tokenInfo.expirationTime.valueOf() - tokenInfo.whenCreated.valueOf()) / 1000, - 'account_name': req.vAuthAccount.username, - 'account_roles': req.vAuthAccount.roles, - 'account_id': req.vAuthAccount.id + "token": tokenInfo.token, + "token_id": tokenInfo.id, + "refresh_token": tokenInfo.refreshToken, + "token_expiration_seconds": (tokenInfo.expirationTime.valueOf() - tokenInfo.whenCreated.valueOf()) / 1000, + "account_name": req.vAuthAccount.username, + "account_roles": req.vAuthAccount.roles, + "account_id": req.vAuthAccount.id }; + } else { + req.vRestResp.respondFailure("incorrect scope"); } - else { - req.vRestResp.respondFailure('incorrect scope'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/token/new'; +export const name = "/api/v1/token/new"; export const router = Router(); -router.post( '/api/v1/token/new', setupMetaverseAPI, - accountFromAuthToken, - procPostTokenNew, - finishMetaverseAPI ); - +router.post("/api/v1/token/new", setupMetaverseAPI, + accountFromAuthToken, + procPostTokenNew, + finishMetaverseAPI); diff --git a/src/routes/api/v1/tokens.ts b/src/routes/api/v1/tokens.ts index 437717ec..6d9cf783 100755 --- a/src/routes/api/v1/tokens.ts +++ b/src/routes/api/v1/tokens.ts @@ -12,23 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict' -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Tokens, TokenScope } from '@Entities/Tokens'; +import { Tokens, TokenScope } from "@Entities/Tokens"; -import { Logger } from '@Tools/Logging'; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { AccountScopeFilter } from '@Entities/EntityFilters/AccountScopeFilter'; +import { Logger } from "@Tools/Logging"; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { AccountScopeFilter } from "@Entities/EntityFilters/AccountScopeFilter"; const procGetTokens: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - Logger.debug('procGetTokens'); + Logger.debug("procGetTokens"); if (req.vAuthAccount) { const pager = new PaginationInfo(); - const scoper = new AccountScopeFilter(req.vAuthAccount, 'accountId'); + const scoper = new AccountScopeFilter(req.vAuthAccount, "accountId"); pager.parametersFromRequest(req); scoper.parametersFromRequest(req); @@ -36,18 +35,18 @@ const procGetTokens: RequestHandler = async (req: Request, resp: Response, next: const toks: any[] = []; for await (const tok of Tokens.enumerateAsync(pager, scoper)) { toks.push({ - 'tokenId': tok.id, - 'id': tok.id, - 'token': tok.token, - 'accountId': tok.accountId, - 'refresh_token': tok.refreshToken, - 'scope': tok.scope, - 'creation_time': tok.whenCreated?.toISOString(), - 'creation_time_s': tok.whenCreated?.getTime().toString(), - 'expiration_time': tok.expirationTime?.toISOString(), - 'expiration_time_s': tok.expirationTime?.getTime().toString() + "tokenId": tok.id, + "id": tok.id, + "token": tok.token, + "accountId": tok.accountId, + "refresh_token": tok.refreshToken, + "scope": tok.scope, + "creation_time": tok.whenCreated?.toISOString(), + "creation_time_s": tok.whenCreated?.getTime().toString(), + "expiration_time": tok.expirationTime?.toISOString(), + "expiration_time_s": tok.expirationTime?.getTime().toString() }); - }; + } req.vRestResp.Data = { tokens: toks @@ -55,18 +54,19 @@ const procGetTokens: RequestHandler = async (req: Request, resp: Response, next: scoper.addResponseFields(req); pager.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/tokens'; +export const name = "/api/v1/tokens"; export const router = Router(); -router.get('/api/v1/tokens', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetTokens, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/tokens", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetTokens, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/connection_request.ts b/src/routes/api/v1/user/connection_request.ts index 56ec4c12..e666ff52 100755 --- a/src/routes/api/v1/user/connection_request.ts +++ b/src/routes/api/v1/user/connection_request.ts @@ -12,20 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; -import { Requests, RequestType } from '@Entities/Requests'; -import { RequestEntity } from '@Entities/RequestEntity'; +import { Requests, RequestType } from "@Entities/Requests"; +import { RequestEntity } from "@Entities/RequestEntity"; -import { Accounts } from '@Entities/Accounts'; -import { accountFromAuthToken, usernameFromParams } from '@Route-Tools/middleware'; +import { Accounts } from "@Entities/Accounts"; +import { accountFromAuthToken, usernameFromParams } from "@Route-Tools/middleware"; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; const procPostUserConnectionRequest: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { @@ -48,19 +47,14 @@ const procPostUserConnectionRequest: RequestHandler = async (req: Request, resp: if (req.vAuthAccount.locationNodeId) { if (req.vAuthAccount.locationNodeId === thisNode) { Logger.debug(`procPostUserConnectionRequest: request from ${req.vAuthAccount.username} and locationNodeid matches main node`); + } else if (req.vAuthAccount.locationNodeId === otherNode) { + Logger.debug(`procPostUserConnectionRequest: request from ${req.vAuthAccount.username} and locationNodeid matches proposed node`); + } else { + Logger.debug(`procPostUserConnectionRequest: request from ${req.vAuthAccount.username} and locationNodeid does not match either node`); } - else { - if (req.vAuthAccount.locationNodeId === otherNode) { - Logger.debug(`procPostUserConnectionRequest: request from ${req.vAuthAccount.username} and locationNodeid matches proposed node`); - } - else { - Logger.debug(`procPostUserConnectionRequest: request from ${req.vAuthAccount.username} and locationNodeid does not match either node`); - }; - }; - } - else { + } else { Logger.debug(`procPostUserConnectionRequest: request from ${req.vAuthAccount.username} and no nodeID info`); - }; + } // END sanity check DEBUG DEBUG let pending = true; // assume request is still pending @@ -68,8 +62,8 @@ const procPostUserConnectionRequest: RequestHandler = async (req: Request, resp: // Find if there is another requests between these two nodes // Note: this is not the accountID but the session nodeId of the avatars const previousAsk = await Requests.getWithRequestBetween(thisNode, otherNode, - RequestType.HANDSHAKE, - 'requesterNodeId', 'targetNodeId'); + RequestType.HANDSHAKE, + "requesterNodeId", "targetNodeId"); if (IsNotNullOrEmpty(previousAsk)) { Logger.debug(`procPostUserConnectionRequest: previous ask. by=${req.vAuthAccount.id}, this=${thisNode}, other=${otherNode}`); // There is an existing connection request @@ -82,55 +76,49 @@ const procPostUserConnectionRequest: RequestHandler = async (req: Request, resp: await BuildConnectionResponse(req, previousAsk.targetAccountId); pending = false; // The request itself will timeout and expire - } - else { + } else { Logger.debug(`procPostUserConnectionRequest: other hasn't accepted. by=${req.vAuthAccount.id}, this=${thisNode}, other=${otherNode}`); - }; - } - else { + } + } else { Logger.debug(`procPostUserConnectionRequest: I accept. by=${req.vAuthAccount.id}, this=${thisNode}, other=${otherNode}`); // There is an existing request to me. // Since I'm making the same request, we are mutually involved previousAsk.targetAccepted = true; previousAsk.targetAccountId = req.vAuthAccount.id; - Requests.update(previousAsk, { 'targetAccepted': true, - 'targetAccountId': req.vAuthAccount.id }) + Requests.update(previousAsk, { "targetAccepted": true, + "targetAccountId": req.vAuthAccount.id }); const areConnected = await BuildNewConnection(previousAsk); if (areConnected) { await BuildConnectionResponse(req, previousAsk.requestingAccountId); pending = false; - } - else { + } else { Logger.error(`procPostUserConnectionRequest: BuildNewConnection failed. by=${req.vAuthAccount.id}, this=${thisNode}, other=${otherNode}`); - req.vRestResp.respondFailure('error making connection'); - }; + req.vRestResp.respondFailure("error making connection"); + } // The request itself will timeout and expire - }; - } - else { + } + } else { // There is not a pending request between us. Create one Logger.debug(`procPostUserConnectionRequest: new request. by=${req.vAuthAccount.id}, this=${thisNode}, other=${otherNode}`); const newRequest = await Requests.createHandshakeRequest(thisNode, otherNode); newRequest.requesterAccepted = true; newRequest.requestingAccountId = req.vAuthAccount.id; await Requests.add(newRequest); - }; + } if (pending) { // The above didn't create a response so we're just waiting req.vRestResp.Data = { - 'connection': 'pending' + "connection": "pending" }; - }; + } + } else { + req.vRestResp.respondFailure("badly formed request"); } - else { - req.vRestResp.respondFailure('badly formed request'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; @@ -142,46 +130,48 @@ async function BuildNewConnection(pRequest: RequestEntity): Promise { if (requestingAccount && targetAccount) { await Accounts.makeAccountsConnected(requestingAccount, targetAccount); wasConnected = true; - } - else { + } else { Logger.error(`connection_request: acceptance for connection but accounts not found`); Logger.error(`connection_request: reqAccId=${pRequest.requestingAccountId}, tgtAccId=${pRequest.targetAccountId}`); - }; + } return wasConnected; -}; +} // Build the response that says a connection has been made async function BuildConnectionResponse(req: Request, pOtherAccountId: string): Promise { const otherAccount = await Accounts.getAccountWithId(pOtherAccountId); req.vRestResp.Data = { - 'connection': { - 'new_connection': true, - 'username': otherAccount ? otherAccount.username : 'UNKNOWN' + "connection": { + "new_connection": true, + "username": otherAccount ? otherAccount.username : "UNKNOWN" } }; -}; +} // A user is asking for all it's handshake requests to be removed const procDeleteUserConnectionRequest: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { Logger.debug(`procDeleteUserConnectionRequest: deleting for ${req.vAuthAccount.id}`); await Requests.removeAllMyRequests(req.vAuthAccount.id, RequestType.HANDSHAKE); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/connection_request'; +export const name = "/api/v1/user/connection_request"; export const router = Router(); -router.post( '/api/v1/user/connection_request', [ setupMetaverseAPI, - accountFromAuthToken, - procPostUserConnectionRequest, - finishMetaverseAPI ] ); -router.delete('/api/v1/user/connection_request', [ setupMetaverseAPI, - accountFromAuthToken, - procDeleteUserConnectionRequest, - finishMetaverseAPI ] ); +router.post("/api/v1/user/connection_request", [ + setupMetaverseAPI, + accountFromAuthToken, + procPostUserConnectionRequest, + finishMetaverseAPI +]); +router["delete"]("/api/v1/user/connection_request", [ + setupMetaverseAPI, + accountFromAuthToken, + procDeleteUserConnectionRequest, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/connections.ts b/src/routes/api/v1/user/connections.ts index 9a3cbaea..0a12b888 100755 --- a/src/routes/api/v1/user/connections.ts +++ b/src/routes/api/v1/user/connections.ts @@ -12,18 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken, param1FromParams } from '@Route-Tools/middleware'; -import { Accounts } from '@Entities/Accounts'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken, param1FromParams } from "@Route-Tools/middleware"; +import { Accounts } from "@Entities/Accounts"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; +import { VKeyedCollection } from "@Tools/vTypes"; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; // Get the connections of the logged in account const procGetUserConnections: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -31,26 +30,25 @@ const procGetUserConnections: RequestHandler = async (req: Request, resp: Respon const pager = new PaginationInfo(); pager.parametersFromRequest(req); - let connections = await Accounts.getField(req.vAuthToken, req.vAuthAccount, 'connections', req.vAuthAccount); + let connections = await Accounts.getField(req.vAuthToken, req.vAuthAccount, "connections", req.vAuthAccount); connections = IsNullOrEmpty(connections) - ? [] // if no connections info, return empty list - : connections; + ? [] // if no connections info, return empty list + : connections; req.vRestResp.Data = { - 'connections': connections + connections }; pager.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; // Upgrade a connection to a friend. // Not implemented as something needs to be done with request_connection, etc const procPostUserConnections: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - req.vRestResp.respondFailure('cannot add connections this way'); + req.vRestResp.respondFailure("cannot add connections this way"); next(); }; @@ -58,30 +56,32 @@ const procPostUserConnections: RequestHandler = async (req: Request, resp: Respo const procDeleteUserConnections: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { await Accounts.removeConnection(req.vAuthAccount, req.vParam1); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/connections'; +export const name = "/api/v1/user/connections"; export const router = Router(); -router.get( '/api/v1/user/connections', [ setupMetaverseAPI, // req.vRestResp, vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetUserConnections, - finishMetaverseAPI - ] ); -router.post( '/api/v1/user/connections', [ setupMetaverseAPI, // req.vRestResp, vAuthToken - accountFromAuthToken, // req.vAuthAccount - procPostUserConnections, - finishMetaverseAPI - ] ); -router.delete('/api/v1/user/connections/:param1', [ setupMetaverseAPI,// req.vRestResp, vAuthToken - accountFromAuthToken, // req.vAuthAccount - param1FromParams, // req.vParam1 - procDeleteUserConnections, - finishMetaverseAPI - ] ); +router.get("/api/v1/user/connections", [ + setupMetaverseAPI, // req.vRestResp, vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetUserConnections, + finishMetaverseAPI +]); +router.post("/api/v1/user/connections", [ + setupMetaverseAPI, // req.vRestResp, vAuthToken + accountFromAuthToken, // req.vAuthAccount + procPostUserConnections, + finishMetaverseAPI +]); +router["delete"]("/api/v1/user/connections/:param1", [ + setupMetaverseAPI, // req.vRestResp, vAuthToken + accountFromAuthToken, // req.vAuthAccount + param1FromParams, // req.vParam1 + procDeleteUserConnections, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/friends.ts b/src/routes/api/v1/user/friends.ts index 4b654cf7..e6587305 100755 --- a/src/routes/api/v1/user/friends.ts +++ b/src/routes/api/v1/user/friends.ts @@ -12,18 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken, param1FromParams } from '@Route-Tools/middleware'; -import { Accounts } from '@Entities/Accounts'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken, param1FromParams } from "@Route-Tools/middleware"; +import { Accounts } from "@Entities/Accounts"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; -import { SArray, VKeyedCollection } from '@Tools/vTypes'; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; +import { SArray, VKeyedCollection } from "@Tools/vTypes"; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; // Get the friends of the logged in account const procGetUserFriends: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { @@ -31,45 +30,41 @@ const procGetUserFriends: RequestHandler = async (req: Request, resp: Response, const pager = new PaginationInfo(); pager.parametersFromRequest(req); - let friends = await Accounts.getField(req.vAuthToken, req.vAuthAccount, 'friends', req.vAuthAccount); + let friends = await Accounts.getField(req.vAuthToken, req.vAuthAccount, "friends", req.vAuthAccount); friends = IsNullOrEmpty(friends) - ? [] // if no friends info, return empty list - : friends; + ? [] // if no friends info, return empty list + : friends; req.vRestResp.Data = { - 'friends': friends + friends }; pager.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; // Upgrade a connection to a friend. const procPostUserFriends: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { - if (req.body.username && typeof(req.body.username) === 'string') { + if (req.body.username && typeof req.body.username === "string") { const newFriend = req.body.username; // Verify the username is a connection. - const connections: string[] = await Accounts.getField(req.vAuthToken, req.vAuthAccount, 'connections', req.vAuthAccount) ?? []; + const connections: string[] = await Accounts.getField(req.vAuthToken, req.vAuthAccount, "connections", req.vAuthAccount) ?? []; if (SArray.hasNoCase(connections, newFriend)) { const updates: VKeyedCollection = {}; - await Accounts.setField(req.vAuthToken, req.vAuthAccount, 'friends', { "add": newFriend }, req.vAuthAccount, updates); + await Accounts.setField(req.vAuthToken, req.vAuthAccount, "friends", { "add": newFriend }, req.vAuthAccount, updates); await Accounts.updateEntityFields(req.vAuthAccount, updates); + } else { + req.vRestResp.respondFailure("cannot add friend who is not a connection"); } - else { - req.vRestResp.respondFailure('cannot add friend who is not a connection'); - }; + } else { + req.vRestResp.respondFailure("badly formed request"); } - else { - req.vRestResp.respondFailure('badly formed request'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; @@ -77,30 +72,32 @@ const procPostUserFriends: RequestHandler = async (req: Request, resp: Response, const procDeleteUserFriends: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { await Accounts.removeFriend(req.vAuthAccount, req.vParam1); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/friends'; +export const name = "/api/v1/user/friends"; export const router = Router(); -router.get( '/api/v1/user/friends', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetUserFriends, - finishMetaverseAPI - ] ); -router.post( '/api/v1/user/friends', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procPostUserFriends, - finishMetaverseAPI - ] ); -router.delete('/api/v1/user/friends/:param1', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - param1FromParams, // req.vParam1 - procDeleteUserFriends, - finishMetaverseAPI - ] ); \ No newline at end of file +router.get("/api/v1/user/friends", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetUserFriends, + finishMetaverseAPI +]); +router.post("/api/v1/user/friends", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procPostUserFriends, + finishMetaverseAPI +]); +router["delete"]("/api/v1/user/friends/:param1", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + param1FromParams, // req.vParam1 + procDeleteUserFriends, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/heartbeat.ts b/src/routes/api/v1/user/heartbeat.ts index e8d5d9a3..d5fc4678 100755 --- a/src/routes/api/v1/user/heartbeat.ts +++ b/src/routes/api/v1/user/heartbeat.ts @@ -12,18 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { updateLocationInfo } from '@Route-Tools/Util'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { updateLocationInfo } from "@Route-Tools/Util"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; const procPutUserHeartbeat: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { @@ -34,24 +33,23 @@ const procPutUserHeartbeat: RequestHandler = async (req: Request, resp: Response if (IsNotNullOrEmpty(req.vAuthAccount.locationNodeId)) { req.vRestResp.Data = { // 'session_id': req.vSession.id - 'session_id': req.vAuthAccount.locationNodeId + "session_id": req.vAuthAccount.locationNodeId }; - }; - }; + } + } + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/heartbeat'; +export const name = "/api/v1/user/heartbeat"; export const router = Router(); -router.put( '/api/v1/user/heartbeat', [ setupMetaverseAPI, - accountFromAuthToken, - procPutUserHeartbeat, - finishMetaverseAPI - ] ); - +router.put("/api/v1/user/heartbeat", [ + setupMetaverseAPI, + accountFromAuthToken, + procPutUserHeartbeat, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/location.ts b/src/routes/api/v1/user/location.ts index f0deae86..8dfd8434 100755 --- a/src/routes/api/v1/user/location.ts +++ b/src/routes/api/v1/user/location.ts @@ -12,37 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { updateLocationInfo } from '@Route-Tools/Util'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { updateLocationInfo } from "@Route-Tools/Util"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { VKeyedCollection } from '@Tools/vTypes'; -import { IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { VKeyedCollection } from "@Tools/vTypes"; +import { IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; const procPutUserLocation: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { const updates = await updateLocationInfo(req); await Accounts.updateEntityFields(req.vAuthAccount, updates); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/location'; +export const name = "/api/v1/user/location"; export const router = Router(); -router.put( '/api/v1/user/location', [ setupMetaverseAPI, - accountFromAuthToken, - procPutUserLocation, - finishMetaverseAPI - ] ); +router.put("/api/v1/user/location", [ + setupMetaverseAPI, + accountFromAuthToken, + procPutUserLocation, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/locker.ts b/src/routes/api/v1/user/locker.ts index 8f776d72..fae63119 100755 --- a/src/routes/api/v1/user/locker.ts +++ b/src/routes/api/v1/user/locker.ts @@ -12,49 +12,49 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { IsNullOrEmpty } from '@Tools/Misc'; -import { Accounts } from '@Entities/Accounts'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { IsNullOrEmpty } from "@Tools/Misc"; +import { Accounts } from "@Entities/Accounts"; const procGetUserLocker: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { if (IsNullOrEmpty(req.vAuthAccount.locker)) { req.vRestResp.Data = {}; // legacy code wants something back - } - else { + } else { req.vRestResp.Data = req.vAuthAccount.locker; - }; + } + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; const procPostUserLocker: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { - await Accounts.updateEntityFields(req.vAuthAccount, { 'locker': req.body } ); + await Accounts.updateEntityFields(req.vAuthAccount, { "locker": req.body }); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/locker'; +export const name = "/api/v1/user/locker"; export const router = Router(); -router.get( '/api/v1/user/locker', [ setupMetaverseAPI, - accountFromAuthToken, - procGetUserLocker, - finishMetaverseAPI ] ); -router.post( '/api/v1/user/locker', [ setupMetaverseAPI, - accountFromAuthToken, - procPostUserLocker, - finishMetaverseAPI ] ); +router.get("/api/v1/user/locker", [ + setupMetaverseAPI, + accountFromAuthToken, + procGetUserLocker, + finishMetaverseAPI +]); +router.post("/api/v1/user/locker", [ + setupMetaverseAPI, + accountFromAuthToken, + procPostUserLocker, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/places.ts b/src/routes/api/v1/user/places.ts index 151f7c1c..a3761fdc 100755 --- a/src/routes/api/v1/user/places.ts +++ b/src/routes/api/v1/user/places.ts @@ -12,25 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import Config from '@Base/config'; +import Config from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams, placeFromParams } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams, placeFromParams } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { procGetPlacesPlaceId, procDeletePlacesPlaceId } from '../places/placeId'; -import { procPostPlaces } from '../places'; +import { procGetPlacesPlaceId, procDeletePlacesPlaceId } from "../places/placeId"; +import { procPostPlaces } from "../places"; -import { buildPlaceInfo } from '@Route-Tools/Util'; +import { buildPlaceInfo } from "@Route-Tools/Util"; -import { Places } from '@Entities/Places'; -import { Maturity } from '@Entities/Sets/Maturity'; +import { Places } from "@Entities/Places"; +import { Maturity } from "@Entities/Sets/Maturity"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { PlaceFilterInfo } from '@Entities/EntityFilters/PlaceFilterInfo'; -import { Domains } from '@Entities/Domains'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { PlaceFilterInfo } from "@Entities/EntityFilters/PlaceFilterInfo"; +import { Domains } from "@Entities/Domains"; const procGetPlaces: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { @@ -47,47 +46,53 @@ const procGetPlaces: RequestHandler = async (req: Request, resp: Response, next: const aDomain = await Domains.getDomainWithId(place.domainId); if (aDomain && aDomain.sponsorAccountId === req.vAuthAccount.id) { allPlaces.push(await buildPlaceInfo(place)); - }; - }; - }; + } + } + } req.vRestResp.Data = { - 'places': allPlaces, - 'maturity-categories': Maturity.MaturityCategories + "places": allPlaces, + "maturity-categories": Maturity.MaturityCategories }; pager.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/places'; +export const name = "/api/v1/user/places"; export const router = Router(); // Some of these are under 'user' as that was a legacy configuration. -router.get( '/api/v1/user/places', [ setupMetaverseAPI, // req.vRESTResp - accountFromAuthToken, // req.vAuthAccount - procGetPlaces, - finishMetaverseAPI ] ); -router.post( '/api/v1/user/places', - [ setupMetaverseAPI, // req.vRESTResp - accountFromAuthToken, // req.vAuthAccount - procPostPlaces, - finishMetaverseAPI ] ); -router.get( '/api/v1/user/places/:placeId', - [ setupMetaverseAPI, // req.vRESTResp - accountFromAuthToken, // req.vAuthAccount - placeFromParams, // req.vPlace, req.vDomain - procGetPlacesPlaceId, - finishMetaverseAPI ] ); -router.delete( '/api/v1/user/places/:placeId', - [ setupMetaverseAPI, // req.vRESTResp - accountFromAuthToken, // req.vAuthAccount - placeFromParams, // req.vPlace, req.vDomain - procDeletePlacesPlaceId, - finishMetaverseAPI ] ); - +router.get("/api/v1/user/places", [ + setupMetaverseAPI, // req.vRESTResp + accountFromAuthToken, // req.vAuthAccount + procGetPlaces, + finishMetaverseAPI +]); +router.post("/api/v1/user/places", + [ + setupMetaverseAPI, // req.vRESTResp + accountFromAuthToken, // req.vAuthAccount + procPostPlaces, + finishMetaverseAPI + ]); +router.get("/api/v1/user/places/:placeId", + [ + setupMetaverseAPI, // req.vRESTResp + accountFromAuthToken, // req.vAuthAccount + placeFromParams, // req.vPlace, req.vDomain + procGetPlacesPlaceId, + finishMetaverseAPI + ]); +router["delete"]("/api/v1/user/places/:placeId", + [ + setupMetaverseAPI, // req.vRESTResp + accountFromAuthToken, // req.vAuthAccount + placeFromParams, // req.vPlace, req.vDomain + procDeletePlacesPlaceId, + finishMetaverseAPI + ]); diff --git a/src/routes/api/v1/user/profile.ts b/src/routes/api/v1/user/profile.ts index 6300f3c1..08394ac9 100755 --- a/src/routes/api/v1/user/profile.ts +++ b/src/routes/api/v1/user/profile.ts @@ -12,41 +12,40 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { IsNotNullOrEmpty } from '@Tools/Misc'; +import { IsNotNullOrEmpty } from "@Tools/Misc"; // Get old profile information. // This request will get greatly expanded. const procGetUserProfile: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { req.vRestResp.Data = { - 'user': { - 'username': req.vAuthAccount.username, - 'accountid': req.vAuthAccount.id, - 'xmpp_password': req.vAuthAccount.xmppPassword, - 'discourse_api_key': req.vAuthAccount.discourseApiKey, - 'wallet_id': req.vAuthAccount.walletId + "user": { + "username": req.vAuthAccount.username, + "accountid": req.vAuthAccount.id, + "xmpp_password": req.vAuthAccount.xmppPassword, + "discourse_api_key": req.vAuthAccount.discourseApiKey, + "wallet_id": req.vAuthAccount.walletId } }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/profile'; +export const name = "/api/v1/user/profile"; export const router = Router(); -router.get( '/api/v1/user/profile', [ setupMetaverseAPI, - accountFromAuthToken, - procGetUserProfile, - finishMetaverseAPI - ] ); \ No newline at end of file +router.get("/api/v1/user/profile", [ + setupMetaverseAPI, + accountFromAuthToken, + procGetUserProfile, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user/public_key.ts b/src/routes/api/v1/user/public_key.ts index 82ce0585..b8b68981 100755 --- a/src/routes/api/v1/user/public_key.ts +++ b/src/routes/api/v1/user/public_key.ts @@ -12,19 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import multer from 'multer'; +import multer from "multer"; -import { Logger } from '@Tools/Logging'; -import { convertBinKeyToPEM } from '@Route-Tools/Util'; -import { Accounts } from '@Entities/Accounts'; -import { IsNotNullOrEmpty } from '@Tools/Misc'; +import { Logger } from "@Tools/Logging"; +import { convertBinKeyToPEM } from "@Route-Tools/Util"; +import { Accounts } from "@Entities/Accounts"; +import { IsNotNullOrEmpty } from "@Tools/Misc"; // PUT /api/v1/user/public_key // The api_key and public_key are POSTed as entities in a multi-part-form mime type. @@ -40,41 +39,39 @@ const procPutUserPublicKey: RequestHandler = async (req: Request, resp: Response if (IsNotNullOrEmpty(publicKeyBin)) { const fieldsToUpdate = { - 'sessionPublicKey': convertBinKeyToPEM(publicKeyBin) + "sessionPublicKey": convertBinKeyToPEM(publicKeyBin) }; await Accounts.updateEntityFields(req.vAuthAccount, fieldsToUpdate); + } else { + req.vRestResp.respondFailure("badly formed public key"); } - else { - req.vRestResp.respondFailure('badly formed public key'); - }; - } - catch (e) { - Logger.error('procPutUserPublicKey: exception converting: ' + e); - req.vRestResp.respondFailure('exception converting public key'); + } catch (e) { + Logger.error("procPutUserPublicKey: exception converting: " + e); + req.vRestResp.respondFailure("exception converting public key"); } + } else { + Logger.error("procPutUserPublicKey: no files part of body"); + req.vRestResp.respondFailure("no public key supplied"); } - else { - Logger.error('procPutUserPublicKey: no files part of body'); - req.vRestResp.respondFailure('no public key supplied'); - }; + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; next(); }; -export const name = '/api/v1/user/public_key'; +export const name = "/api/v1/user/public_key"; export const router = Router(); // The public key is sent in binary in a multipart-form. // This creates an unpacker to catch fields 'api_key' and 'public_key' const multiStorage = multer.memoryStorage(); -const uploader = multer( { storage: multiStorage, }); -const multiParser = uploader.fields( [ { name: 'public_key' }]); -router.put( '/api/v1/user/public_key', [ setupMetaverseAPI, - accountFromAuthToken, // vRESTResp.vAuthAccount - multiParser, // body['api_key'], files['public_key'].buffer - procPutUserPublicKey, - finishMetaverseAPI ] ); \ No newline at end of file +const uploader = multer({ storage: multiStorage }); +const multiParser = uploader.fields([{ name: "public_key" }]); +router.put("/api/v1/user/public_key", [ + setupMetaverseAPI, + accountFromAuthToken, // vRESTResp.vAuthAccount + multiParser, // body['api_key'], files['public_key'].buffer + procPutUserPublicKey, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user_activities.ts b/src/routes/api/v1/user_activities.ts index 86c3f8d4..e30c2ee4 100755 --- a/src/routes/api/v1/user_activities.ts +++ b/src/routes/api/v1/user_activities.ts @@ -12,32 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; const procPostUserActivities: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.body.action_name) { const activity = req.body.action_name; if (req.vAuthAccount) { Logger.debug(`procPostUserActivities: Received user activity "${activity}" from ${req.vAuthAccount.username}`); - } - else { + } else { Logger.debug(`procPostUserActivities: Received user activity "${activity}" from unknown user`); - }; - }; + } + } next(); }; -export const name = '/api/v1/user_activities'; +export const name = "/api/v1/user_activities"; export const router = Router(); -router.post( '/api/v1/user_activities', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procPostUserActivities, - finishMetaverseAPI ] ); \ No newline at end of file +router.post("/api/v1/user_activities", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procPostUserActivities, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/user_stories.ts b/src/routes/api/v1/user_stories.ts index aa2df16b..952dc5ee 100755 --- a/src/routes/api/v1/user_stories.ts +++ b/src/routes/api/v1/user_stories.ts @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; // Sample request from Interface scripts // GET /api/v1/user_stories?include_actions=announcement&restriction=open,hifi&require_online=true&protocol=Qa12Qv2cYYxmNV8c%2Fqlf5A%3D%3D&per_page=10 4 @@ -27,10 +26,12 @@ const procGetUserStories: RequestHandler = async (req: Request, resp: Response, next(); }; -export const name = '/api/v1/user_stories'; +export const name = "/api/v1/user_stories"; export const router = Router(); -router.get('/api/v1/user_stories', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - procGetUserStories, - finishMetaverseAPI ] ); +router.get("/api/v1/user_stories", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + procGetUserStories, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/users.ts b/src/routes/api/v1/users.ts index c7244f40..7fec2a5e 100755 --- a/src/routes/api/v1/users.ts +++ b/src/routes/api/v1/users.ts @@ -12,61 +12,59 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Accounts } from '@Entities/Accounts'; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { AccountScopeFilter } from '@Entities/EntityFilters/AccountScopeFilter'; -import { AccountFilterInfo } from '@Entities/EntityFilters/AccountFilterInfo'; +import { Accounts } from "@Entities/Accounts"; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { AccountScopeFilter } from "@Entities/EntityFilters/AccountScopeFilter"; +import { AccountFilterInfo } from "@Entities/EntityFilters/AccountFilterInfo"; -import { buildUserInfo } from '@Route-Tools/Util'; +import { buildUserInfo } from "@Route-Tools/Util"; -import { Logger } from '@Tools/Logging'; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Roles } from '@Entities/Sets/Roles'; -import { SArray } from '@Tools/vTypes'; +import { Logger } from "@Tools/Logging"; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Roles } from "@Entities/Sets/Roles"; +import { SArray } from "@Tools/vTypes"; // metaverseServerApp.use(express.urlencoded({ extended: false })); // Get basic user information const procGetUsers: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vAuthAccount) { - const pager = new PaginationInfo(); - const scoper = new AccountScopeFilter(req.vAuthAccount); - const infoer = new AccountFilterInfo(); - pager.parametersFromRequest(req); - scoper.parametersFromRequest(req); - infoer.parametersFromRequest(req); - - // Loop through all the filtered accounts and create array of info - const accts: any[] = []; - for await (const acct of Accounts.enumerateAsync(scoper, infoer, pager)) { - const userInfo = await buildUserInfo(acct); - if (req.vAuthAccount.id === acct.id) { - // Add a tag to denote the requestor's entry - userInfo.connection = "self"; - }; - accts.push(userInfo); - }; - - req.vRestResp.Data = { - users: accts - }; - - pager.addResponseFields(req); - infoer.addResponseFields(req); - scoper.addResponseFields(req); - } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; - next(); + if (req.vAuthAccount) { + const pager = new PaginationInfo(); + const scoper = new AccountScopeFilter(req.vAuthAccount); + const infoer = new AccountFilterInfo(); + pager.parametersFromRequest(req); + scoper.parametersFromRequest(req); + infoer.parametersFromRequest(req); + + // Loop through all the filtered accounts and create array of info + const accts: any[] = []; + for await (const acct of Accounts.enumerateAsync(scoper, infoer, pager)) { + const userInfo = await buildUserInfo(acct); + if (req.vAuthAccount.id === acct.id) { + // Add a tag to denote the requestor's entry + userInfo.connection = "self"; + } + accts.push(userInfo); + } + + req.vRestResp.Data = { + users: accts + }; + + pager.addResponseFields(req); + infoer.addResponseFields(req); + scoper.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); + } + next(); }; // Create a user account using the username and password passed @@ -78,9 +76,9 @@ const procPostUsers: RequestHandler = async (req: Request, resp: Response, next: const userEmail: string = req.body.user.email; Logger.debug(`procPostUsers: request to create account for ${userName} with email ${userEmail}`); // Precheck format of username and email before trying to set them - let ifValid = await Accounts.validateFieldValue('username', userName); + let ifValid = await Accounts.validateFieldValue("username", userName); if (ifValid.valid) { - ifValid = await Accounts.validateFieldValue('email', userEmail); + ifValid = await Accounts.validateFieldValue("email", userEmail); if (ifValid.valid) { // See if account already exists let prevAccount = await Accounts.getAccountWithUsername(userName); @@ -93,12 +91,14 @@ const procPostUsers: RequestHandler = async (req: Request, resp: Response, next: // A special kludge to create the initial admin account // If we're creating an account that has the name of the admin account // then add the 'admin' role to it. - const adminAccountName = Config["metaverse-server"]["base-admin-account"]; + const adminAccountName = Config["metaverse-server"]["base-admin-account"]; if (IsNotNullOrEmpty(adminAccountName)) { if (newAcct.username === adminAccountName) { - if (IsNullOrEmpty(newAcct.roles)) newAcct.roles = []; - SArray.add(newAcct.roles, Roles.ADMIN); - Logger.info(`procPostUsers: setting new account ${adminAccountName} as admin`); + if (IsNullOrEmpty(newAcct.roles)) { + newAcct.roles = []; + } + SArray.add(newAcct.roles, Roles.ADMIN); + Logger.info(`procPostUsers: setting new account ${adminAccountName} as admin`); } } // Remember the address of the creator @@ -116,48 +116,45 @@ const procPostUsers: RequestHandler = async (req: Request, resp: Response, next: accountIsActive, accountWaitingVerification: !accountIsActive }; + } catch (err) { + Logger.error("procPostUsers: exception adding user: " + err); + req.vRestResp.respondFailure("Exception adding user: " + err); } - catch (err) { - Logger.error('procPostUsers: exception adding user: ' + err); - req.vRestResp.respondFailure('Exception adding user: ' + err); - } + } else { + Logger.debug("procPostUsers: error creating account for " + userName); + req.vRestResp.respondFailure("could not create account"); } - else { - Logger.debug('procPostUsers: error creating account for ' + userName); - req.vRestResp.respondFailure('could not create account'); - }; + } else { + req.vRestResp.respondFailure("Email already exists"); } - else { - req.vRestResp.respondFailure('Email already exists'); - }; + } else { + req.vRestResp.respondFailure("Account already exists"); } - else { - req.vRestResp.respondFailure('Account already exists'); - }; + } else { + req.vRestResp.respondFailure(ifValid.reason ?? "Badly formatted email"); } - else { - req.vRestResp.respondFailure(ifValid.reason ?? 'Badly formatted email'); - }; + } else { + req.vRestResp.respondFailure(ifValid.reason ?? "Badly formatted username"); } - else { - req.vRestResp.respondFailure(ifValid.reason ?? 'Badly formatted username'); - }; + } else { + req.vRestResp.respondFailure("Badly formatted request"); } - else { - req.vRestResp.respondFailure('Badly formatted request'); - }; - }; + } next(); }; -export const name = '/api/v1/users'; +export const name = "/api/v1/users"; export const router = Router(); -router.get( '/api/v1/users', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetUsers, - finishMetaverseAPI ] ); -router.post( '/api/v1/users', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - procPostUsers, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/users", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetUsers, + finishMetaverseAPI +]); +router.post("/api/v1/users", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + procPostUsers, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/users/connections.ts b/src/routes/api/v1/users/connections.ts index ccaf0d32..9f318172 100755 --- a/src/routes/api/v1/users/connections.ts +++ b/src/routes/api/v1/users/connections.ts @@ -12,74 +12,71 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { Accounts } from '@Entities/Accounts'; +import { Accounts } from "@Entities/Accounts"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; -import { buildLocationInfo, buildImageInfo } from '@Route-Tools/Util'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; +import { buildLocationInfo, buildImageInfo } from "@Route-Tools/Util"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; -import { SArray, VKeyedCollection } from '@Tools/vTypes'; -import { IsNullOrEmpty, IsNotNullOrEmpty } from '@Tools/Misc'; -import { Logger } from '@Tools/Logging'; +import { SArray, VKeyedCollection } from "@Tools/vTypes"; +import { IsNullOrEmpty, IsNotNullOrEmpty } from "@Tools/Misc"; +import { Logger } from "@Tools/Logging"; // Get the connections of the logged in account const procGetUsersConnections: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vAuthAccount) { - const pager = new PaginationInfo(); - pager.parametersFromRequest(req); + if (req.vAuthAccount) { + const pager = new PaginationInfo(); + pager.parametersFromRequest(req); - let connections: string[] = await Accounts.getField(req.vAuthToken, req.vAuthAccount, 'connections', req.vAuthAccount); - connections = IsNullOrEmpty(connections) - ? [] // if no connections info, return empty list - : connections; + let connections: string[] = await Accounts.getField(req.vAuthToken, req.vAuthAccount, "connections", req.vAuthAccount); + connections = IsNullOrEmpty(connections) + ? [] // if no connections info, return empty list + : connections; - Logger.debug(`procGetUsersConnections: user=${req.vAuthAccount.username}, connections=${JSON.stringify(connections)}`); - const connectionInfo: any[] = []; - for (const connectionUsername of connections) { - if (pager.criteriaTest(connectionUsername)) { - const aAccount = await Accounts.getAccountWithUsername(connectionUsername); - if (aAccount) { - connectionInfo.push( { - 'username': connectionUsername, - 'connection': SArray.has(req.vAuthAccount.friends, connectionUsername) ? 'friend' : 'connection', - 'images': await buildImageInfo(aAccount), - 'location': await buildLocationInfo(aAccount) - }); + Logger.debug(`procGetUsersConnections: user=${req.vAuthAccount.username}, connections=${JSON.stringify(connections)}`); + const connectionInfo: any[] = []; + for (const connectionUsername of connections) { + if (pager.criteriaTest(connectionUsername)) { + const aAccount = await Accounts.getAccountWithUsername(connectionUsername); + if (aAccount) { + connectionInfo.push({ + "username": connectionUsername, + "connection": SArray.has(req.vAuthAccount.friends, connectionUsername) ? "friend" : "connection", + "images": await buildImageInfo(aAccount), + "location": await buildLocationInfo(aAccount) + }); + } else { + Logger.error(`procGetUsersConnections: connection name with no account. acct=${req.vAuthAccount.id}, name=${connectionUsername}`); + connectionInfo.push({ + "username": connectionUsername, + "connection": "unknown" + }); + } + } } - else { - Logger.error(`procGetUsersConnections: connection name with no account. acct=${req.vAuthAccount.id}, name=${connectionUsername}`); - connectionInfo.push( { - 'username': connectionUsername, - 'connection': 'unknown' - }); + req.vRestResp.Data = { + "users": connectionInfo }; - }; - }; - req.vRestResp.Data = { - 'users': connectionInfo - }; - // Add the 'current_page' and 'total_pages' to the response - pager.addResponseFields(req); - } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; - next(); + // Add the 'current_page' and 'total_pages' to the response + pager.addResponseFields(req); + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); + } + next(); }; -export const name = '/api/v1/users/connections'; +export const name = "/api/v1/users/connections"; export const router = Router(); -router.get('/api/v1/users/connections', [ setupMetaverseAPI, - accountFromAuthToken, - procGetUsersConnections, - finishMetaverseAPI - ] ); - +router.get("/api/v1/users/connections", [ + setupMetaverseAPI, + accountFromAuthToken, + procGetUsersConnections, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/users/public_key.ts b/src/routes/api/v1/users/public_key.ts index 9dcf5182..a1cd50d1 100755 --- a/src/routes/api/v1/users/public_key.ts +++ b/src/routes/api/v1/users/public_key.ts @@ -12,38 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; -import { accountFromParams } from '@Route-Tools/middleware'; +import { accountFromParams } from "@Route-Tools/middleware"; -import { Logger } from '@Tools/Logging'; -import { createSimplifiedPublicKey, convertBinKeyToPEM } from '@Route-Tools/Util'; +import { Logger } from "@Tools/Logging"; +import { createSimplifiedPublicKey, convertBinKeyToPEM } from "@Route-Tools/Util"; // metaverseServerApp.use(express.urlencoded({ extended: false })); const procGetUsersPublicKey: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vAccount) { - req.vRestResp.Data = { - 'public_key': createSimplifiedPublicKey(req.vAccount.sessionPublicKey), - 'username': req.vAccount.username, - 'accountid': req.vAccount.id - }; - } - else { - req.vRestResp.respondFailure('Target account not found'); - }; - next(); + if (req.vAccount) { + req.vRestResp.Data = { + "public_key": createSimplifiedPublicKey(req.vAccount.sessionPublicKey), + "username": req.vAccount.username, + "accountid": req.vAccount.id + }; + } else { + req.vRestResp.respondFailure("Target account not found"); + } + next(); }; -export const name = '/api/v1/users/:accoundId/public_key'; +export const name = "/api/v1/users/:accoundId/public_key"; export const router = Router(); -router.get( '/api/v1/users/:accountId/public_key', [ setupMetaverseAPI, - accountFromParams, // vRESTResp.vAccount - procGetUsersPublicKey, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/api/v1/users/:accountId/public_key", [ + setupMetaverseAPI, + accountFromParams, // vRESTResp.vAccount + procGetUsersPublicKey, + finishMetaverseAPI +]); diff --git a/src/routes/api/v1/users/username/location.ts b/src/routes/api/v1/users/username/location.ts index f80c4045..80b13bee 100755 --- a/src/routes/api/v1/users/username/location.ts +++ b/src/routes/api/v1/users/username/location.ts @@ -12,46 +12,44 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken, accountFromParams } from '@Route-Tools/middleware'; -import { buildLocationInfo } from '@Route-Tools/Util'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken, accountFromParams } from "@Route-Tools/middleware"; +import { buildLocationInfo } from "@Route-Tools/Util"; -import { Perm } from '@Route-Tools/Perm'; -import { checkAccessToEntity } from '@Route-Tools/Permissions'; +import { Perm } from "@Route-Tools/Perm"; +import { checkAccessToEntity } from "@Route-Tools/Permissions"; const procGetUserLocation: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vAuthAccount) { - if (req.vAccount) { - if (await checkAccessToEntity(req.vAuthToken, req.vAccount, - [ Perm.OWNER, Perm.FRIEND, Perm.CONNECTION, Perm.ADMIN ], req.vAuthAccount)) { - req.vRestResp.Data = { - 'location': await buildLocationInfo(req.vAccount) - }; - } - else { - req.vRestResp.respondFailure('unauthorized'); - } + if (req.vAuthAccount) { + if (req.vAccount) { + if (await checkAccessToEntity(req.vAuthToken, req.vAccount, + [Perm.OWNER, Perm.FRIEND, Perm.CONNECTION, Perm.ADMIN], req.vAuthAccount)) { + req.vRestResp.Data = { + "location": await buildLocationInfo(req.vAccount) + }; + } else { + req.vRestResp.respondFailure("unauthorized"); + } + } else { + req.vRestResp.respondFailure("target account not found"); + } + } else { + req.vRestResp.respondFailure(req.vAccountError ?? "Not logged in"); } - else { - req.vRestResp.respondFailure('target account not found'); - }; - } - else { - req.vRestResp.respondFailure(req.vAccountError ?? 'Not logged in'); - }; - next(); + next(); }; -export const name = '/api/v1/users/:accountId/location'; +export const name = "/api/v1/users/:accountId/location"; export const router = Router(); // Note the user of :accountId which looks up the username -router.get( '/api/v1/users/:accountId/location', [ setupMetaverseAPI, // req.vRESTReq - accountFromAuthToken, // req.vAuthAccount - accountFromParams, // req.vAccount - procGetUserLocation, - finishMetaverseAPI ]); \ No newline at end of file +router.get("/api/v1/users/:accountId/location", [ + setupMetaverseAPI, // req.vRESTReq + accountFromAuthToken, // req.vAuthAccount + accountFromParams, // req.vAccount + procGetUserLocation, + finishMetaverseAPI +]); diff --git a/src/routes/explore.ts b/src/routes/explore.ts index 79266bb9..14aab64f 100755 --- a/src/routes/explore.ts +++ b/src/routes/explore.ts @@ -12,23 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, finishReturnData } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, finishReturnData } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Accounts } from '@Entities/Accounts'; -import { Places } from '@Entities/Places'; -import { Domains } from '@Entities/Domains'; +import { Accounts } from "@Entities/Accounts"; +import { Places } from "@Entities/Places"; +import { Domains } from "@Entities/Domains"; -import { PaginationInfo } from '@Entities/EntityFilters/PaginationInfo'; -import { PlaceFilterInfo } from '@Entities/EntityFilters/PlaceFilterInfo'; +import { PaginationInfo } from "@Entities/EntityFilters/PaginationInfo"; +import { PlaceFilterInfo } from "@Entities/EntityFilters/PlaceFilterInfo"; -import { IsNotNullOrEmpty, IsNullOrEmpty } from '@Tools/Misc'; -import { VKeyedCollection } from '@Tools/vTypes'; -import { Logger } from '@Tools/Logging'; -import { buildPlaceInfoSmall } from '@Route-Tools/Util'; +import { IsNotNullOrEmpty, IsNullOrEmpty } from "@Tools/Misc"; +import { VKeyedCollection } from "@Tools/vTypes"; +import { Logger } from "@Tools/Logging"; +import { buildPlaceInfoSmall } from "@Route-Tools/Util"; const procGetExplore: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { const pager = new PaginationInfo(); @@ -42,23 +41,23 @@ const procGetExplore: RequestHandler = async (req: Request, resp: Response, next const aDomain = await Domains.getDomainWithId(place.domainId); if (aDomain && IsNotNullOrEmpty(aDomain.networkAddr)) { const placeDesc: VKeyedCollection = { - 'Domain Name': place.name, + "Domain Name": place.name }; placeDesc.Address = await Places.getAddressString(place); - placeDesc.Visit = 'hifi://' + placeDesc.Address; + placeDesc.Visit = "hifi://" + placeDesc.Address; placeDesc.DomainId = aDomain.id; - placeDesc['Network Address'] = aDomain.networkAddr; - placeDesc['Network Port'] = aDomain.networkPort; + placeDesc["Network Address"] = aDomain.networkAddr; + placeDesc["Network Port"] = aDomain.networkPort; // If there is a sponsoring account, add the domain owner to the place description - placeDesc.Owner = ''; + placeDesc.Owner = ""; if (IsNotNullOrEmpty(aDomain.sponsorAccountId)) { const aAccount = await Accounts.getAccountWithId(aDomain.sponsorAccountId); if (IsNotNullOrEmpty(aAccount)) { placeDesc.Owner = aAccount.username; - }; - }; + } + } // 'People' is number of people at place for old Explore script // placeDesc.People = aDomain.numUsers; @@ -67,8 +66,8 @@ const procGetExplore: RequestHandler = async (req: Request, resp: Response, next placeDesc.Place = await buildPlaceInfoSmall(place, aDomain); allPlaces.push(placeDesc); - }; - }; + } + } req.vRestResp.Data = allPlaces; placer.addResponseFields(req); @@ -77,12 +76,13 @@ const procGetExplore: RequestHandler = async (req: Request, resp: Response, next next(); }; -export const name = '/explore'; +export const name = "/explore"; export const router = Router(); -router.get( '/explore.json', [ setupMetaverseAPI, // req.vRESTResp, req.vAuthToken - accountFromAuthToken, // req.vAuthAccount - procGetExplore, - finishReturnData - ] ); \ No newline at end of file +router.get("/explore.json", [ + setupMetaverseAPI, // req.vRESTResp, req.vAuthToken + accountFromAuthToken, // req.vAuthAccount + procGetExplore, + finishReturnData +]); diff --git a/src/routes/oauth/token.ts b/src/routes/oauth/token.ts index 5415e034..7ec5d90a 100755 --- a/src/routes/oauth/token.ts +++ b/src/routes/oauth/token.ts @@ -12,25 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import Config from '@Base/config'; +import Config from "@Base/config"; -import bodyParser from 'express'; +import bodyParser from "express"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, finishReturnData } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, finishReturnData } from "@Route-Tools/middleware"; -import { AccountEntity } from '@Entities/AccountEntity'; -import { Accounts } from '@Entities/Accounts'; -import { Roles } from '@Entities/Sets/Roles'; -import { AuthToken } from '@Entities/AuthToken'; -import { Tokens, TokenScope } from '@Entities/Tokens'; +import { AccountEntity } from "@Entities/AccountEntity"; +import { Accounts } from "@Entities/Accounts"; +import { Roles } from "@Entities/Sets/Roles"; +import { AuthToken } from "@Entities/AuthToken"; +import { Tokens, TokenScope } from "@Entities/Tokens"; -import { IsNullOrEmpty } from '@Tools/Misc'; -import { VKeyedCollection } from '@Tools/vTypes'; +import { IsNullOrEmpty } from "@Tools/Misc"; +import { VKeyedCollection } from "@Tools/vTypes"; -import { Logger } from '@Tools/Logging'; +import { Logger } from "@Tools/Logging"; // Do a 'login' and return an initial access token for a user // Request comes as a 'application/x-www-form-urlencoded' body. @@ -42,7 +41,7 @@ const procPostOauthToken: RequestHandler = async (req: Request, resp: Response, try { const accessGrantType = req.body.grant_type; switch (accessGrantType) { - case 'password': { + case "password": { // There are several types of "password"s passed by Interface: // PLAIN PASSWORD const userName = req.body.username; @@ -61,42 +60,38 @@ const procPostOauthToken: RequestHandler = async (req: Request, resp: Response, if (IsNullOrEmpty(aAccount)) { // people can log in with either their username or their email aAccount = await Accounts.getAccountWithEmail(userName); - }; + } if (aAccount) { if (Accounts.isEnabled(aAccount)) { if (await Accounts.validatePassword(aAccount, userPassword)) { Logger.debug(`procPostOAuthToken: login of user ${userName}`); - const tokenInfo = await Tokens.createToken(aAccount.id, [ userScope ]); + const tokenInfo = await Tokens.createToken(aAccount.id, [userScope]); await Tokens.addToken(tokenInfo); respBody = buildOAuthResponseBody(aAccount, tokenInfo); - } - else { - respBody = buildOAuthErrorBody('Invalid password'); + } else { + respBody = buildOAuthErrorBody("Invalid password"); req.vRestResp.IsFailure = true; - }; - } - else { - respBody = buildOAuthErrorBody('Account not verified'); + } + } else { + respBody = buildOAuthErrorBody("Account not verified"); req.vRestResp.IsFailure = true; - }; - } - else{ - respBody = buildOAuthErrorBody('Unknown user'); + } + } else { + respBody = buildOAuthErrorBody("Unknown user"); req.vRestResp.IsFailure = true; - }; - } - else { - respBody = buildOAuthErrorBody('Invalid scope'); + } + } else { + respBody = buildOAuthErrorBody("Invalid scope"); req.vRestResp.IsFailure = true; - }; + } break; - }; - case 'authorization_code': { - respBody = buildOAuthErrorBody('Do not know what to do with an authorization_code'); + } + case "authorization_code": { + respBody = buildOAuthErrorBody("Do not know what to do with an authorization_code"); req.vRestResp.IsFailure = true; break; - }; - case 'refresh_token': { + } + case "refresh_token": { const refreshingToken = req.body.refresh_token; const refreshToken = await Tokens.getTokenWithRefreshToken(refreshingToken); if (Tokens.hasNotExpired(refreshToken)) { @@ -106,29 +101,26 @@ const procPostOauthToken: RequestHandler = async (req: Request, resp: Response, const newToken = await Tokens.createToken(req.vAuthAccount.id, refreshToken.scope); await Tokens.addToken(newToken); respBody = buildOAuthResponseBody(requestingAccount, newToken); - } - else { - respBody = buildOAuthErrorBody('refresh token not owned by accessing account'); + } else { + respBody = buildOAuthErrorBody("refresh token not owned by accessing account"); req.vRestResp.IsFailure = true; - }; - } - else { - respBody = buildOAuthErrorBody('refresh token expired'); + } + } else { + respBody = buildOAuthErrorBody("refresh token expired"); req.vRestResp.IsFailure = true; } break; - }; + } default: { - respBody = buildOAuthErrorBody('Unknown grant_type: ' + accessGrantType); + respBody = buildOAuthErrorBody("Unknown grant_type: " + accessGrantType); req.vRestResp.IsFailure = true; break; - }; - }; - } - catch (err) { - respBody = buildOAuthErrorBody('Exception: ' + err); + } + } + } catch (err) { + respBody = buildOAuthErrorBody("Exception: " + err); req.vRestResp.IsFailure = true; - }; + } req.vRestResp.Data = respBody; next(); @@ -138,34 +130,36 @@ const procPostOauthToken: RequestHandler = async (req: Request, resp: Response, // This is also used by /api/v1/token/new to return the created token. export function buildOAuthResponseBody(pAcct: AccountEntity, pToken: AuthToken): VKeyedCollection { const body: VKeyedCollection = { - 'access_token': pToken.token, - 'token_type': 'Bearer', - 'expires_in': pToken.expirationTime.valueOf()/1000 - pToken.whenCreated.valueOf()/1000, - 'refresh_token': pToken.refreshToken, - 'scope': pToken.scope[0], - 'created_at': pToken.whenCreated.valueOf() / 1000, + "access_token": pToken.token, + "token_type": "Bearer", + "expires_in": pToken.expirationTime.valueOf() / 1000 - pToken.whenCreated.valueOf() / 1000, + "refresh_token": pToken.refreshToken, + "scope": pToken.scope[0], + "created_at": pToken.whenCreated.valueOf() / 1000 }; if (pAcct) { body.account_id = pAcct.id, body.account_name = pAcct.username, body.account_roles = pAcct.roles; - }; + } return body; -}; +} function buildOAuthErrorBody(pMsg: string): VKeyedCollection { Logger.error(`procPostOauthToken: returning error body: ${pMsg}`); return { - 'error': pMsg + "error": pMsg }; -}; +} export const name = "/oauth/token"; export const router = Router(); -router.post( '/oauth/token', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - bodyParser.urlencoded({extended: true}), // req.body - procPostOauthToken, - finishReturnData ]); +router.post("/oauth/token", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + bodyParser.urlencoded({ extended: true }), // req.body + procPostOauthToken, + finishReturnData +]); diff --git a/src/routes/user/places.ts b/src/routes/user/places.ts index ffdd35f4..c43ee47e 100755 --- a/src/routes/user/places.ts +++ b/src/routes/user/places.ts @@ -12,13 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from '@Route-Tools/middleware'; -import { HTTPStatusCode } from '@Route-Tools/RESTResponse'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, param1FromParams } from "@Route-Tools/middleware"; +import { HTTPStatusCode } from "@Route-Tools/RESTResponse"; // When a user gots to "METAVERSE_URL/user/places", redirect to // "https://dashboard.vircadia.com?metaverse=METAVERSE_URL&page=places" @@ -27,15 +26,17 @@ const procGetPlaces: RequestHandler = async (req: Request, resp: Response, next: const metaverseURL = encodeURIComponent(Config.metaverse["metaverse-server-url"]); const redirectionURL = `${dashboardURL}?metaverse=${metaverseURL}&page=places`; resp.statusCode = HTTPStatusCode.Found; - resp.setHeader('Location', redirectionURL), - resp.setHeader('content-type', 'text/html'); + resp.setHeader("Location", redirectionURL), + resp.setHeader("content-type", "text/html"); resp.send(); }; -export const name = '/user/places'; +export const name = "/user/places"; export const router = Router(); -router.get( '/user/places', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - procGetPlaces, - finishMetaverseAPI ] ); \ No newline at end of file +router.get("/user/places", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + procGetPlaces, + finishMetaverseAPI +]); diff --git a/src/routes/user/tokens/new.ts b/src/routes/user/tokens/new.ts index e2f3f760..517f2560 100755 --- a/src/routes/user/tokens/new.ts +++ b/src/routes/user/tokens/new.ts @@ -12,45 +12,43 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI } from '@Route-Tools/middleware'; -import { accountFromAuthToken } from '@Route-Tools/middleware'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI } from "@Route-Tools/middleware"; +import { accountFromAuthToken } from "@Route-Tools/middleware"; -import { Tokens, TokenScope } from '@Entities/Tokens'; -import { HTTPStatusCode } from '@Route-Tools/RESTResponse'; +import { Tokens, TokenScope } from "@Entities/Tokens"; +import { HTTPStatusCode } from "@Route-Tools/RESTResponse"; -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; const procGetUserTokensNew: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { if (req.vAuthAccount) { const forDomainServer = req.query.for_domain_server; const scope = forDomainServer ? TokenScope.DOMAIN : TokenScope.OWNER; - const tokenInfo = await Tokens.createToken(req.vAuthAccount.id, [ scope ]); + const tokenInfo = await Tokens.createToken(req.vAuthAccount.id, [scope]); await Tokens.addToken(tokenInfo); const body = `

Your domain's access token is ${tokenInfo.token}

`; - resp.setHeader('content-type', 'text/html'); + resp.setHeader("content-type", "text/html"); resp.send(body); - } - else { + } else { // if the user is not logged in, go to a page to login and set things up let tokengenURL = Config["metaverse-server"].tokengen_url; - tokengenURL = tokengenURL.replace('METAVERSE_SERVER_URL', Config.metaverse['metaverse-server-url']); - tokengenURL = tokengenURL.replace('DASHBOARD_URL', Config.metaverse['dashboard-url']); + tokengenURL = tokengenURL.replace("METAVERSE_SERVER_URL", Config.metaverse["metaverse-server-url"]); + tokengenURL = tokengenURL.replace("DASHBOARD_URL", Config.metaverse["dashboard-url"]); resp.statusCode = HTTPStatusCode.Found; - resp.setHeader('Location', tokengenURL); - resp.setHeader('content-type', 'text/html'); + resp.setHeader("Location", tokengenURL); + resp.setHeader("content-type", "text/html"); resp.send(); - }; + } }; -export const name = '/user/tokens/new'; +export const name = "/user/tokens/new"; export const router = Router(); -router.get( '/user/tokens/new', setupMetaverseAPI, // req.vRestResp, req.vAuthToken - accountFromAuthToken, - procGetUserTokensNew); +router.get("/user/tokens/new", setupMetaverseAPI, // req.vRestResp, req.vAuthToken + accountFromAuthToken, + procGetUserTokensNew); diff --git a/src/routes/users/username.ts b/src/routes/users/username.ts index 2b9dba65..6e523f3e 100755 --- a/src/routes/users/username.ts +++ b/src/routes/users/username.ts @@ -12,38 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -'use strict'; -import { Config } from '@Base/config'; +import { Config } from "@Base/config"; -import { Router, RequestHandler, Request, Response, NextFunction } from 'express'; -import { setupMetaverseAPI, finishMetaverseAPI, usernameFromParams } from '@Route-Tools/middleware'; -import { HTTPStatusCode } from '@Route-Tools/RESTResponse'; +import { Router, RequestHandler, Request, Response, NextFunction } from "express"; +import { setupMetaverseAPI, finishMetaverseAPI, usernameFromParams } from "@Route-Tools/middleware"; +import { HTTPStatusCode } from "@Route-Tools/RESTResponse"; // When a user goes to "METAVERSE_URL/users/:username", redirect to // "https://dashboard.vircadia.com?metaverse=METAVERSE_URL&page=user/profile&user=username" const procGetUserProfile: RequestHandler = async (req: Request, resp: Response, next: NextFunction) => { - if (req.vUsername && typeof(req.vUsername) === 'string') { + if (req.vUsername && typeof req.vUsername === "string") { const dashboardURL = Config.metaverse["dashboard-url"]; const metaverseURL = encodeURIComponent(Config.metaverse["metaverse-server-url"]); const username = encodeURIComponent(req.vUsername); const redirectionURL = `${dashboardURL}?metaverse=${metaverseURL}&page=profile/${username}`; resp.statusCode = HTTPStatusCode.Found; - resp.setHeader('Location', redirectionURL), - resp.setHeader('content-type', 'text/html'); + resp.setHeader("Location", redirectionURL), + resp.setHeader("content-type", "text/html"); resp.send(); - } - else { - req.vRestResp.respondFailure('usename not specified'); + } else { + req.vRestResp.respondFailure("usename not specified"); } next(); }; -export const name = '/users/:username'; +export const name = "/users/:username"; export const router = Router(); -router.get( '/users/:username', [ setupMetaverseAPI, // req.vRestResp, req.vAuthToken - usernameFromParams, // req.vUsername - procGetUserProfile, - finishMetaverseAPI ] ); +router.get("/users/:username", [ + setupMetaverseAPI, // req.vRestResp, req.vAuthToken + usernameFromParams, // req.vUsername + procGetUserProfile, + finishMetaverseAPI +]); diff --git a/tests/Entities/Accounts.unit.test.ts b/tests/Entities/Accounts.unit.test.ts new file mode 100644 index 00000000..bf36fb5a --- /dev/null +++ b/tests/Entities/Accounts.unit.test.ts @@ -0,0 +1,96 @@ +import { MongoClient } from "mongodb"; + +import { AccountEntity } from "../../src/Entities/AccountEntity"; +import { Accounts } from "../../src/Entities/Accounts"; +import { setDB } from "../../src/Tools/Db"; + +async function testDatab() { + const connectUrl = process.env.CONNECT_URL; + const dbName = process.env.DB_NAME; + + const baseClient = await MongoClient.connect(connectUrl, { + useUnifiedTopology: true, + useNewUrlParser: true + }); + setDB(baseClient.db(dbName)); +} + +function mockAccount(user: string): AccountEntity { + return { + id: "id", + username: user, + email: user + "@test.com", + accountSettings: null, + imagesHero: null, + imagesThumbnail: null, + imagesTiny: null, + + locationConnected: null, + locationPath: null, + locationPlaceId: null, + locationDomainId: null, + locationNetworkAddress: null, + locationNetworkPort: null, + locationNodeId: null, + availability: null, + + connections: null, + friends: null, + locker: null, + profileDetail: null, + + passwordHash: null, + passwordSalt: null, + sessionPublicKey: null, + accountEmailVerified: true, + + xmppPassword: null, + discourseApiKey: null, + walletId: null, + + roles: null, + IPAddrOfCreator: null, + whenCreated: null, + timeOfLastHeartbeat: null + + }; +} + +describe("Entities/Accounts - unit tests", () => { + test("getAccountWithId returns null with invalid input", () => { + testDatab() + .then(() => { + return Accounts.getAccountWithId("").then((resp) => { + expect(resp).toBeNull(); + }); + }); + }); + + test("getAccountWithId returns null with no inserted accounts", () => { + testDatab() + .then(() => { + return Accounts.getAccountWithId("id").then((resp) => { + expect(resp).toBeNull(); + }); + }); + }); + + test("addAccount, then geting it by ID, then removing it", () => { + testDatab() + .then(() => { + const accExpected = mockAccount("test3"); + Accounts.addAccount(accExpected) + .then((acc) => { + Accounts.getAccountWithId(acc.id) + .then((fetched) => { + expect(fetched.username).toBe(accExpected.username); + expect(fetched.email).toBe(accExpected.email); + + return Accounts.removeAccount(fetched).then((resp) => { + expect(resp).toBe(true); + }); + }); + }); + }); + }); +}); diff --git a/tslint.json b/tslint.json deleted file mode 100755 index 35baa870..00000000 --- a/tslint.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "jsRules": {}, - "rules": { - "trailing-comma": [ false ] - }, - "strict-boolean-expressions": [ - true, - "allow-boolean-or-undefined" - ], - "rulesDirectory": [] -}