diff --git a/.eslintignore b/.eslintignore index 012a3cd..3c3629e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1 @@ -index.js +node_modules diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 62% rename from .eslintrc.js rename to .eslintrc.cjs index f46e70c..7003a65 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -1,18 +1,12 @@ -export default { - root: true, +module.exports = { env: { browser: true, - es6: true, + es2021: true, node: true, }, - extends: ['eslint:recommended', 'prettier'], - plugins: ['import'], - globals: { - Atomics: 'readonly', - SharedArrayBuffer: 'readonly', - }, + extends: 'standard', parserOptions: { - ecmaVersion: 2018, + ecmaVersion: 'latest', sourceType: 'module', }, rules: { @@ -22,5 +16,7 @@ export default { 'no-restricted-syntax': ['off'], 'no-continue': ['off'], 'quote-props': ['warn', 'consistent-as-needed'], + 'semi': ['warn', 'always'], + 'comma-dangle': ['warn', 'always-multiline'], }, }; diff --git a/.gitignore b/.gitignore index f607946..12d80e2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ public/styles/* .yarn dump.rdb docker/redis-volume/* +.env diff --git a/index.js b/index.js index 72057a1..d62dee3 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,5 @@ import 'dotenv/config'; -// require('dotenv/config'); -// j = require = require('esm')(module /*, options*/); + if (process.env.WORKERS > 1 || process.env.WORKERS === -1) { import('./src/web/index.cluster.js'); } else { diff --git a/package-lock.json b/package-lock.json index 37b7e3e..3eac914 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "body-parser": "^1.19.0", + "composable-state": "^0.1.0", "dotenv": "^15.0.0", "express": "^4.17.2", "ferp": "^2.1.2", @@ -21,6 +22,11 @@ }, "devDependencies": { "ava": "^4.3.3", + "eslint": "^8.42.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^16.0.0", + "eslint-plugin-promise": "^6.1.1", "husky": "^7.0.0", "nodemon": "^2.0.15", "nyc": "^15.1.0", @@ -397,6 +403,140 @@ "node": ">=6.9.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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 + }, "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", @@ -594,6 +734,12 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -624,6 +770,15 @@ "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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", @@ -678,6 +833,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -746,6 +917,19 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -760,6 +944,25 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -769,6 +972,42 @@ "node": ">=8" } }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arrgv": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", @@ -857,6 +1096,18 @@ } } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -962,6 +1213,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1039,9 +1299,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001503", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz", + "integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==", "dev": true, "funding": [ { @@ -1051,6 +1311,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -1287,6 +1551,11 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, + "node_modules/composable-state": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/composable-state/-/composable-state-0.1.0.tgz", + "integrity": "sha512-Mp/SnbzQvu/2TuZONwWIEyuJA0gJlHiNa6nPJItFkW6Ny7i7U43ZYrRKypHFGLsfmvIiG3Q/uY5qDjVVQtVhQg==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1440,6 +1709,12 @@ "node": ">=0.10.0" } }, + "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/default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", @@ -1455,6 +1730,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/defined": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", @@ -1615,6 +1906,18 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dotenv": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-15.0.1.tgz", @@ -1666,6 +1969,94 @@ "node": ">= 0.8" } }, + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "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/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -1698,43 +2089,594 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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, + "node_modules/eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", "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" + } + ], "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" } }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-6.2.1.tgz", + "integrity": "sha512-uR34zUhZ9EBoiSD2DdV5kHLpydVEvwWqjteUr9sXRgJknwbKZJZhdJ7uFnaTtd+Nr/2G3ceJHnHXrFhJ67n3Tw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/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/eslint-plugin-n": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.0.0.tgz", + "integrity": "sha512-akkZTE3hsHBrq6CwmGuYCzQREbVUrA855kzcHqe6i0FLBkeY7Y/6tThCVkjUnjhvRBAlc+8lILcSe5QvvDpeZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^6.1.0", + "ignore": "^5.1.1", + "is-core-module": "^2.12.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", + "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "dev": 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": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", "body-parser": "1.20.1", "content-disposition": "0.5.4", "content-type": "~1.0.4", @@ -1819,6 +2761,12 @@ "node": ">= 0.8" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "node_modules/fast-diff": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", @@ -1840,6 +2788,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -1869,6 +2829,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1940,6 +2912,34 @@ "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, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2013,6 +3013,33 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/generic-pool": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", @@ -2061,6 +3088,22 @@ "node": ">=8.0.0" } }, + "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.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2101,6 +3144,21 @@ "node": ">=4" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "13.1.3", "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", @@ -2132,12 +3190,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2149,6 +3225,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "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", @@ -2158,6 +3243,30 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -2169,6 +3278,21 @@ "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/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -2258,6 +3382,31 @@ "node": ">=10 <11 || >=12 <13 || >=14" } }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2294,6 +3443,20 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.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", @@ -2311,6 +3474,32 @@ "node": ">=8" } }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2322,10 +3511,38 @@ "node": ">=8" } }, + "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.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dependencies": { "has": "^1.0.3" }, @@ -2333,6 +3550,21 @@ "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-error": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", @@ -2370,6 +3602,18 @@ "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", @@ -2378,6 +3622,21 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -2411,6 +3670,34 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "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.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "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", @@ -2423,6 +3710,55 @@ "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-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "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", @@ -2441,6 +3777,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -2669,6 +4017,18 @@ "node": ">=4" } }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2687,6 +4047,19 @@ "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", @@ -2737,6 +4110,12 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2944,6 +4323,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -3326,13 +4711,57 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "engines": { - "node": ">= 6" + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "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.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3357,6 +4786,23 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -3459,6 +4905,27 @@ "node": ">=8" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module/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/parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", @@ -3779,6 +5246,15 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -3824,6 +5300,15 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -3922,6 +5407,23 @@ "@redis/time-series": "1.0.4" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -3950,11 +5452,11 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4051,15 +5553,29 @@ } ] }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "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/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4388,6 +5904,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", @@ -4412,6 +5973,18 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supertap": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", @@ -4524,6 +6097,12 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/time-zone": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", @@ -4573,6 +6152,51 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -4603,6 +6227,20 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -4612,6 +6250,21 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -4652,6 +6305,15 @@ "browserslist": ">= 4.21.0" } }, + "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, + "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", @@ -4706,12 +6368,57 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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", @@ -5251,6 +6958,99 @@ "to-fast-properties": "^2.0.0" } }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "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 + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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 + } + } + }, + "@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -5413,6 +7213,12 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -5434,6 +7240,13 @@ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "acorn-node": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", @@ -5472,6 +7285,18 @@ "indent-string": "^5.0.0" } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -5522,6 +7347,16 @@ "sprintf-js": "~1.0.2" } }, + "array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + } + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -5533,12 +7368,49 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, "arrgv": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", @@ -5604,6 +7476,12 @@ "yargs": "^17.5.1" } }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -5679,10 +7557,19 @@ "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + } + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" } }, "bytes": { @@ -5743,9 +7630,9 @@ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, "caniuse-lite": { - "version": "1.0.30001457", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz", - "integrity": "sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==", + "version": "1.0.30001503", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz", + "integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==", "dev": true }, "cbor": { @@ -5920,6 +7807,11 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, + "composable-state": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/composable-state/-/composable-state-0.1.0.tgz", + "integrity": "sha512-Mp/SnbzQvu/2TuZONwWIEyuJA0gJlHiNa6nPJItFkW6Ny7i7U43ZYrRKypHFGLsfmvIiG3Q/uY5qDjVVQtVhQg==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6034,6 +7926,12 @@ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "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 + }, "default-require-extensions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", @@ -6043,6 +7941,16 @@ "strip-bom": "^4.0.0" } }, + "define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, "defined": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", @@ -6156,6 +8064,15 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dotenv": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-15.0.1.tgz", @@ -6195,6 +8112,79 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, + "es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + } + }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + } + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "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" + } + }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -6218,12 +8208,389 @@ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "dev": true }, + "eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "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, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": 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 + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "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": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-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 + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } + }, + "eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-es-x": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-6.2.1.tgz", + "integrity": "sha512-uR34zUhZ9EBoiSD2DdV5kHLpydVEvwWqjteUr9sXRgJknwbKZJZhdJ7uFnaTtd+Nr/2G3ceJHnHXrFhJ67n3Tw==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.5.0" + } + }, + "eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dev": true, + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-n": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.0.0.tgz", + "integrity": "sha512-akkZTE3hsHBrq6CwmGuYCzQREbVUrA855kzcHqe6i0FLBkeY7Y/6tThCVkjUnjhvRBAlc+8lILcSe5QvvDpeZQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^6.1.0", + "ignore": "^5.1.1", + "is-core-module": "^2.12.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.0" + } + }, + "eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.2.tgz", + "integrity": "sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==", + "dev": 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", @@ -6318,6 +8685,12 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, "fast-diff": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", @@ -6336,6 +8709,18 @@ "micromatch": "^4.0.4" } }, + "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": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -6359,6 +8744,15 @@ "is-unicode-supported": "^1.2.0" } }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -6417,6 +8811,31 @@ "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, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -6460,6 +8879,24 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, "generic-pool": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", @@ -6493,6 +8930,16 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "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.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -6521,6 +8968,15 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, "globby": { "version": "13.1.3", "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", @@ -6542,12 +8998,27 @@ } } }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -6556,17 +9027,47 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, + "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" + } + }, "hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -6626,6 +9127,24 @@ "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", "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, + "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 + } + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6653,6 +9172,17 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.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", @@ -6664,6 +9194,26 @@ "integrity": "sha512-YXxECO/W6N9aMBVKMKKZ8TXESgq7EFrp3emCGGUcrYY1cgJIeZjoB75MTu8qi+NAKntS9NwPU8VdcQ3r6E6aWQ==", "dev": true }, + "is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -6672,14 +9222,39 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "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-error": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", @@ -6705,11 +9280,26 @@ "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==" }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -6734,12 +9324,62 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "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.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.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-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -6752,6 +9392,15 @@ "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -6929,6 +9578,18 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "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 + }, + "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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6941,6 +9602,16 @@ "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "lilconfig": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", @@ -6979,6 +9650,12 @@ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "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 + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7119,6 +9796,12 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -7433,6 +10116,35 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -7450,6 +10162,20 @@ "wrappy": "1" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -7516,6 +10242,23 @@ "release-zalgo": "^1.0.0" } }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, "parse-ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", @@ -7708,6 +10451,12 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "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 + }, "pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -7741,6 +10490,12 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -7804,6 +10559,17 @@ "@redis/time-series": "1.0.4" } }, + "regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + } + }, "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", @@ -7826,11 +10592,11 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -7877,15 +10643,26 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8143,6 +10920,39 @@ "strip-ansi": "^7.0.1" } }, + "string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, "strip-ansi": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", @@ -8158,6 +10968,12 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "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 + }, "supertap": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", @@ -8241,6 +11057,12 @@ "minimatch": "^3.0.4" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "time-zone": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", @@ -8275,6 +11097,44 @@ "nopt": "~1.0.10" } }, + "tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -8296,6 +11156,17 @@ "mime-types": "~2.1.24" } }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -8305,6 +11176,18 @@ "is-typedarray": "^1.0.0" } }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -8326,6 +11209,15 @@ "picocolors": "^1.0.0" } }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": 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", @@ -8362,12 +11254,45 @@ "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" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "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", diff --git a/package.json b/package.json index 9dea648..c3644c7 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "1.4.0", "main": "index.js", "type": "module", + "engine": ">=16", "license": "MIT", "repository": { "type": "git", @@ -23,6 +24,7 @@ }, "dependencies": { "body-parser": "^1.19.0", + "composable-state": "^0.1.0", "dotenv": "^15.0.0", "express": "^4.17.2", "ferp": "^2.1.2", @@ -34,9 +36,14 @@ }, "devDependencies": { "ava": "^4.3.3", + "eslint": "^8.42.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^16.0.0", + "eslint-plugin-promise": "^6.1.1", "husky": "^7.0.0", - "nyc": "^15.1.0", "nodemon": "^2.0.15", + "nyc": "^15.1.0", "sinon": "^12.0.1" }, "ava": { diff --git a/public/.eslintrc.js b/public/.eslintrc.js deleted file mode 100644 index 59d63e7..0000000 --- a/public/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - rules: { - 'import/no-unresolved': ['off'], - }, -}; diff --git a/public/actions.goal.test.js b/public/actions.goal.test.js index 5237f83..d0cf395 100644 --- a/public/actions.goal.test.js +++ b/public/actions.goal.test.js @@ -32,7 +32,7 @@ test('can add goal', t => { text: goalTextToAdd, completed: false, }); - t.is(state.goal, ''); + t.is(state.goal, 'foo'); t.deepEqual( effect, effects.UpdateGoals({ @@ -220,31 +220,3 @@ test('can rename goal', t => { }), ); }); - -test('can prompt to rename goal', t => { - const initialState = { - goals: [makeGoal('foo')], - externals: { socketEmitter: {} }, - }; - - const [state, effect] = actions.RenameGoalPrompt(initialState, { - id: initialState.goals[0].id, - }); - - t.is(state, initialState); - - t.deepEqual( - effect, - effects.andThen({ - action: actions.PromptOpen, - props: { - text: 'Rename foo to...', - defaultValue: initialState.goals[0].text, - OnValue: actions.RenameGoal, - context: { - id: initialState.goals[0].id, - }, - }, - }), - ); -}); diff --git a/public/actions.init.test.js.md b/public/actions.init.test.js.md index 9b95f82..9fb0953 100644 --- a/public/actions.init.test.js.md +++ b/public/actions.init.test.js.md @@ -10,11 +10,18 @@ Generated by [AVA](https://avajs.dev). [ { - addMultiple: false, allowNotification: false, allowSound: false, currentTime: null, dark: undefined, + details: { + advancedSettings: false, + goalForm: false, + localSettings: false, + mobForm: false, + summary: false, + timerSettings: false, + }, drag: { active: false, clientX: null, @@ -28,8 +35,31 @@ Generated by [AVA](https://avajs.dev). Notification: {}, documentElement: {}, }, + forms: { + goal: { + id: '', + input: '', + valid: true, + }, + mob: { + id: '', + input: '', + valid: true, + }, + mobOrder: { + id: '', + input: 'Navigator,Driver', + valid: true, + }, + timerDuration: { + id: '', + input: '05:00', + valid: true, + }, + }, goal: '', goals: [], + hasInteractedWithPage: false, lang: { goals: { add: 'Add', @@ -105,16 +135,20 @@ Generated by [AVA](https://avajs.dev). }, }, }, + loading: { + isFirstConnection: false, + messages: [ + 'settings:update', + 'mob:update', + 'goals:update', + 'timer:update', + 'connections:update', + ], + total: 5, + }, mob: [], name: '', pendingSettings: {}, - prompt: { - OnValue: Function Noop {}, - context: null, - text: '', - value: '', - visible: false, - }, qrImage: null, settings: { duration: 300000, @@ -131,6 +165,7 @@ Generated by [AVA](https://avajs.dev). [ Function CheckSettingsFX {}, { + onAllowSound: Function SetAllowSound {}, onDarkEnabled: Function SetDark {}, onLocalSoundEnabled: Function SoundToast {}, storage: undefined, diff --git a/public/actions.init.test.js.snap b/public/actions.init.test.js.snap index db8d148..9cedefd 100644 Binary files a/public/actions.init.test.js.snap and b/public/actions.init.test.js.snap differ diff --git a/public/actions.js b/public/actions.js index 1794ba0..4e6f19b 100644 --- a/public/actions.js +++ b/public/actions.js @@ -1,5 +1,6 @@ import * as effects from './effects.js'; import { calculateTimeRemaining } from './lib/calculateTimeRemaining.js'; +import { formatTime } from './lib/formatTime.js'; import * as i18n from './i18n/index.js'; export const Noop = state => state; @@ -13,14 +14,6 @@ const emptyDrag = { clientY: null, }; -const emptyPrompt = { - text: '', - value: '', - context: null, - OnValue: Noop, - visible: false, -}; - export const collectionMove = (collection, { from, to }) => { const newCollection = collection.reduce((memo, item, index) => { if (index === from) return memo; @@ -35,6 +28,16 @@ export const collectionMove = (collection, { from, to }) => { return newCollection.filter(item => item !== null); }; +const defaults = { + timerMobOrder: 'Navigator,Driver', + timerDuration: 5 * 60 * 1000, +}; + +export const CombineActions = (init, actions) => actions.reduce(([state, ...effects], action) => { + const [nextState, ...otherEffects] = [].concat(Array.isArray(action) ? action[0](state, action[1]) : action(state)); + return [nextState, ...effects, ...otherEffects]; +}, [].concat(init)); + export const Init = (_, { timerId, externals, dark, lang }) => [ { timerStartedAt: null, @@ -42,18 +45,52 @@ export const Init = (_, { timerId, externals, dark, lang }) => [ mob: [], goals: [], settings: { - mobOrder: 'Navigator,Driver', - duration: 5 * 60 * 1000, + mobOrder: defaults.timerMobOrder, + duration: defaults.timerDuration, + }, + loading: { + total: 5, + messages: ['settings:update', 'mob:update', 'goals:update', 'timer:update', 'connections:update'], + isFirstConnection: false, + }, + forms: { + mob: { + valid: true, + id: '', + input: '', + }, + goal: { + valid: true, + id: '', + input: '', + }, + timerDuration: { + valid: true, + id: '', + input: formatTime(defaults.timerDuration), + }, + mobOrder: { + valid: true, + id: '', + input: defaults.timerMobOrder, + }, + }, + details: { + summary: false, + mobForm: false, + goalForm: false, + advancedSettings: false, + localSettings: false, + timerSettings: false, }, expandedReorderable: null, timerTab: 'overview', drag: { ...emptyDrag }, - prompt: { ...emptyPrompt }, + // prompt: { ...emptyPrompt }, timerId, currentTime: null, name: '', goal: '', - addMultiple: false, allowNotification: externals.Notification && externals.Notification.permission === 'granted', allowSound: false, @@ -65,14 +102,15 @@ export const Init = (_, { timerId, externals, dark, lang }) => [ dark, lang: i18n.withMissing(i18n[lang]) || i18n.en_CA, qrImage: null, + hasInteractedWithPage: false, }, effects.checkSettings({ storage: externals.storage, + onAllowSound: SetAllowSound, onLocalSoundEnabled: SoundToast, onDarkEnabled: SetDark, }), - dark && - effects.andThen({ + dark && effects.andThen({ action: SetDark, props: { dark }, }), @@ -83,6 +121,66 @@ export const Init = (_, { timerId, externals, dark, lang }) => [ }), ]; +export const SetPageInteraction = (state, _props) => ({ + ...state, + hasInteractedWithPage: true, +}); + +export const DetailsToggle = (state, { which, open }) => ({ + ...state, + details: { + ...state.details, + [which]: Boolean(open), + }, +}); + +export const FirstConnection = state => ({ + ...state, + details: { + ...state.details, + summary: true, + advancedSettings: true, + }, +}); + +export const GoalDoubleClick = (state, { id }) => { + return CombineActions(state, [ + [SetFormId, { form: 'goal', id, input: state.goals.find(g => g.id === id)?.text }], + [DetailsToggle, { which: 'goalForm', open: true }], + ]); +}; + +export const MobDoubleClick = (state, { id }) => { + return CombineActions(state, [ + [SetFormId, { form: 'mob', id, input: state.mob.find(m => m.id === id)?.name }], + [DetailsToggle, { which: 'mobForm', open: true }], + ]); +}; + +export const SetFormId = (state, { form, id, input }) => ({ + ...state, + forms: { + ...state.forms, + [form]: { + ...state.forms[form], + id: state.forms[form].id === id ? '' : id, + input: state.forms[form].id === id ? '' : input || '', + }, + }, +}); + +export const SetFormInput = (state, { form, input, valid }) => ({ + ...state, + forms: { + ...state.forms, + [form]: { + ...state.forms[form], + input, + valid: valid === undefined ? true : Boolean(valid), + }, + }, +}); + export const OnQrLoad = (state, { img }) => [{ ...state, qrImage: img }]; export const SetDark = (state, { dark }) => [ @@ -105,11 +203,6 @@ export const TestSound = state => [ }), ]; -export const SetAddMultiple = (state, addMultiple) => ({ - ...state, - addMultiple: Boolean(addMultiple), -}); - export const SetCurrentTime = (state, { currentTime }) => { const nextState = { ...state, @@ -131,51 +224,51 @@ export const ExpandReorderable = (state, { expandedReorderable }) => ({ expandedReorderable, }); -export const PromptOpen = ( - state, - { text, defaultValue, OnValue, context }, -) => ({ - ...state, - prompt: { - text, - value: defaultValue, - OnValue, - context, - visible: true, - }, -}); - -export const PromptValueChange = (state, value) => ({ - ...state, - prompt: { - ...state.prompt, - value, - }, -}); - -export const PromptOK = state => [ - { - ...state, - prompt: { ...emptyPrompt }, - }, - effects.andThen({ - action: state.prompt.OnValue, - props: { - ...state.prompt.context, - value: state.prompt.value, - }, - }), -]; - -export const PromptCancel = state => ({ - ...state, - prompt: { ...emptyPrompt }, -}); - -export const SetTimerTab = (state, timerTab) => ({ - ...state, - timerTab, -}); +// export const PromptOpen = ( +// state, +// { text, defaultValue, OnValue, context }, +// ) => ({ +// ...state, +// prompt: { +// text, +// value: defaultValue, +// OnValue, +// context, +// visible: true, +// }, +// }); +// +// export const PromptValueChange = (state, value) => ({ +// ...state, +// prompt: { +// ...state.prompt, +// value, +// }, +// }); +// +// export const PromptOK = state => [ +// { +// ...state, +// prompt: { ...emptyPrompt }, +// }, +// effects.andThen({ +// action: state.prompt.OnValue, +// props: { +// ...state.prompt.context, +// value: state.prompt.value, +// }, +// }), +// ]; +// +// export const PromptCancel = state => ({ +// ...state, +// prompt: { ...emptyPrompt }, +// }); +// +// export const SetTimerTab = (state, timerTab) => ({ +// ...state, +// timerTab, +// }); export const DragSelect = (state, { type, from, clientX, clientY }) => ({ ...state, @@ -253,6 +346,30 @@ export const EndTurn = state => [ }), ]; +export const TimeComplete = (state) => { + const nextState = { + ...state, + timerStartedAt: null, + timerDuration: 0, + }; + return [ + nextState, + effects.apiTimerComplete({ + timerId: state.timerId, + completeToken: state.completeToken, + fetch: state.externals.fetch, + onSuccess: { + action: CycleMob, + props: {}, + }, + }), + effects.UpdateTitleWithTime({ + remainingTime: 0, + documentElement: state.externals.documentElement, + }), + ]; +}; + export const Completed = (state, { isEndOfTurn }) => { const nextState = { ...state, @@ -306,25 +423,25 @@ export const RenameUser = (state, { id, value }) => { ]; }; -export const RenameUserPrompt = (state, { id }) => { - const user = state.mob.find(m => m.id === id); - if (!user) return state; - - return [ - state, - effects.andThen({ - action: PromptOpen, - props: { - text: `Rename ${user.name} to...`, - defaultValue: user.name, - OnValue: RenameUser, - context: { - id, - }, - }, - }), - ]; -}; +// export const RenameUserPrompt = (state, { id }) => { +// const user = state.mob.find(m => m.id === id); +// if (!user) return state; +// +// return [ +// state, +// effects.andThen({ +// action: PromptOpen, +// props: { +// text: `Rename ${user.name} to...`, +// defaultValue: user.name, +// OnValue: RenameUser, +// context: { +// id, +// }, +// }, +// }), +// ]; +// }; export const UpdateName = (state, name) => ({ ...state, @@ -386,17 +503,26 @@ export const CycleMob = state => { ]; }; -export const AddNameToMob = state => { +export const AddNameToMob = (state, { name }) => { + if (!name) return state; + const mob = state.mob.concat({ id: Math.random() .toString(36) .slice(2), - name: state.name, + name, }); return [ { ...state, + forms: { + ...state.forms, + mob: { + ...state.forms.mob, + input: '', + }, + }, mob, name: '', }, @@ -450,7 +576,13 @@ export const AddGoal = state => { { ...state, goals, - goal: '', + forms: { + ...state.forms, + goal: { + ...state.goal, + input: '', + }, + }, }, effects.UpdateGoals({ socketEmitter: state.externals.socketEmitter, @@ -477,7 +609,13 @@ export const AddGoals = (state, goals) => { { ...state, goals: allGoals, - goal: '', + forms: { + ...state.forms, + goal: { + ...state.goal, + input: '', + }, + }, }, effects.UpdateGoals({ socketEmitter: state.externals.socketEmitter, @@ -565,26 +703,26 @@ export const RenameGoal = (state, { id, value }) => { }), ]; }; -export const RenameGoalPrompt = (state, { id }) => { - const goal = state.goals.find(g => g.id === id); - if (!goal) return state; - - return [ - state, - effects.andThen({ - action: PromptOpen, - props: { - text: `Rename ${goal.text.length > 32 ? goal.text.slice(0, 29) + '...' : goal.text - } to...`, - defaultValue: goal.text, - OnValue: RenameGoal, - context: { - id, - }, - }, - }), - ]; -}; +// export const RenameGoalPrompt = (state, { id }) => { +// const goal = state.goals.find(g => g.id === id); +// if (!goal) return state; +// +// return [ +// state, +// effects.andThen({ +// action: PromptOpen, +// props: { +// text: `Rename ${goal.text.length > 32 ? goal.text.slice(0, 29) + '...' : goal.text +// } to...`, +// defaultValue: goal.text, +// OnValue: RenameGoal, +// context: { +// id, +// }, +// }, +// }), +// ]; +// }; export const UpdateGoalText = (state, goal) => [ { @@ -604,9 +742,11 @@ export const PauseTimer = (state, currentTime = Date.now()) => { timerDuration, currentTime, }, - effects.PauseTimer({ - socketEmitter: state.externals.socketEmitter, + effects.apiTimerPause({ + timerId: state.timerId, timerDuration, + completeToken: state.completeToken, + fetch: state.externals.fetch, }), ]; }; @@ -617,9 +757,10 @@ export const ResumeTimer = (state, timerStartedAt = Date.now()) => [ timerStartedAt, currentTime: timerStartedAt, }, - effects.StartTimer({ - socketEmitter: state.externals.socketEmitter, - timerDuration: state.timerDuration, + effects.apiTimerStart({ + timerId: state.timerId, + duration: state.timerDuration, + fetch: state.externals.fetch, }), ]; @@ -630,9 +771,10 @@ export const StartTimer = (state, { timerStartedAt, timerDuration }) => [ currentTime: timerStartedAt, timerDuration, }, - effects.StartTimer({ - socketEmitter: state.externals.socketEmitter, - timerDuration, + effects.apiTimerStart({ + timerId: state.timerId, + duration: timerDuration, + fetch: state.externals.fetch, }), ]; @@ -651,6 +793,17 @@ export const SetAllowNotification = (state, { allowNotification }) => [ }), ]; +export const TestNotification = state => [ + state, + effects.Notify({ + title: 'Mobtime Config', + text: 'This is a test notification', + sound: state.allowSound, + Notification: state.externals.Notification, + documentElement: state.externals.documentElement, + }), +]; + export const UpdateNotificationPermissions = state => [ { ...state, @@ -820,6 +973,18 @@ export const PendingSettingsSet = (state, { key, value }) => ({ }, }); +export const SubmitSettings = (state, settings) => [ + state, + effects.apiUpdateSettings({ + timerId: state.timerId, + settings: { + ...state.settings, + ...settings, + }, + fetch: state.externals.fetch, + }), +]; + export const UpdateSettings = state => { const settings = { ...state.settings, @@ -839,41 +1004,113 @@ export const UpdateSettings = state => { ]; }; +export const RevertSettings = state => { + return { + ...state, + forms: { + ...state.forms, + timerDuration: { + ...state.forms.timerDuration, + input: formatTime(state.settings.duration), + }, + mobOrder: { + ...state.forms.mobOrder, + input: state.settings.mobOrder, + }, + }, + }; +}; + +export const LoadingMessagesPop = (state, { type, ...others }) => { + const messages = state.loading.messages.filter(m => m !== type); + const first = type === 'connections:update' + ? { isFirstConnection: others.count === 1 } + : {}; + + const loading = { + ...state.loading, + ...first, + messages, + }; + + console.log('Loaded', type, loading); + + return [ + { + ...state, + loading, + }, + messages.length === 0 && effects.andThen({ + action: RevertSettings, + props: {}, + }), + messages.length === 0 && loading.isFirstConnection && effects.andThen({ + action: FirstConnection, + props: {}, + }), + ]; +}; + export const UpdateByWebsocketData = (state, { payload }) => { const { type, ...data } = payload; switch (type) { + case 'connections:update': + return [ + state, + state.loading.messages.length > 0 && effects.andThen({ + action: LoadingMessagesPop, + props: payload, + }), + ]; case 'settings:update': - return { - ...state, - settings: data.settings, - }; + return [ + { + ...state, + settings: { + ...state.settings, + ...data.settings, + }, + }, + state.loading.messages.length > 0 && effects.andThen({ + action: LoadingMessagesPop, + props: { type }, + }), + ]; case 'timer:start': - return { - ...state, - timerStartedAt: Date.now(), - timerDuration: data.timerDuration, - }; + return [ + { + ...state, + timerStartedAt: Date.now(), + timerDuration: data.timerDuration, + completeToken: data.completeToken, + }, + ]; case 'timer:pause': - return { - ...state, - timerStartedAt: null, - timerDuration: data.timerDuration, - }; + return [ + { + ...state, + timerStartedAt: null, + timerDuration: data.timerDuration, + }, + ]; case 'timer:update': - return { - ...state, - timerStartedAt: data.timerStartedAt, - timerDuration: data.timerDuration, - }; + return [ + { + ...state, + timerStartedAt: data.timerStartedAt, + timerDuration: data.timerDuration, + completeToken: data.completeToken, + }, + state.loading.messages.length > 0 && effects.andThen({ + action: LoadingMessagesPop, + props: { type }, + }), + ]; case 'timer:complete': - if (state.timerStartedAt === null && state.timerDuration === 0) { - return state; - } - return [ state, effects.andThen({ @@ -883,16 +1120,28 @@ export const UpdateByWebsocketData = (state, { payload }) => { ]; case 'goals:update': - return { - ...state, - goals: data.goals, - }; + return [ + { + ...state, + goals: data.goals, + }, + state.loading.messages.length > 0 && effects.andThen({ + action: LoadingMessagesPop, + props: { type }, + }), + ]; case 'mob:update': - return { - ...state, - mob: data.mob, - }; + return [ + { + ...state, + mob: data.mob, + }, + state.loading.messages.length > 0 && effects.andThen({ + action: LoadingMessagesPop, + props: { type }, + }), + ]; default: // console.warn('Unknown websocket data', payload); // eslint-disable-line no-console diff --git a/public/actions.misc.test.js b/public/actions.misc.test.js index 57f8e2a..dea8ada 100644 --- a/public/actions.misc.test.js +++ b/public/actions.misc.test.js @@ -11,11 +11,6 @@ test('can set expand reorderable', t => { t.deepEqual(state, { expandedReorderable }); }); -test('can set timer tab', t => { - const timerTab = 'foo-bar-baz'; - const state = actions.SetTimerTab({}, timerTab); - t.deepEqual(state, { timerTab }); -}); test('can allow notifications', t => { const Notification = {}; @@ -120,15 +115,3 @@ test('can end turn', t => { }), ]); }); - -test('it can toggle addMultiple', t => { - const initialState = { - addMultiple: false, - }; - - const state = actions.SetAddMultiple(initialState, true); - - t.deepEqual(state, { - addMultiple: true, - }); -}); diff --git a/public/actions.mob-rename.test.js b/public/actions.mob-rename.test.js index 6dd97ff..9adeefe 100644 --- a/public/actions.mob-rename.test.js +++ b/public/actions.mob-rename.test.js @@ -40,34 +40,6 @@ test('can rename a user', t => { ); }); -test('can prompt to rename a user', t => { - const mobber = makeUser('Foo'); - - const initialState = { - mob: [mobber], - }; - - const [state, effect] = actions.RenameUserPrompt(initialState, { - id: mobber.id, - }); - - t.deepEqual(state, initialState); - t.deepEqual( - effect, - effects.andThen({ - action: actions.PromptOpen, - props: { - text: 'Rename Foo to...', - defaultValue: 'Foo', - OnValue: actions.RenameUser, - context: { - id: mobber.id, - }, - }, - }), - ); -}); - test('can update in-memory name', t => { const initialState = { name: '' }; diff --git a/public/actions.mob.test.js b/public/actions.mob.test.js index 10001d2..70ad6e1 100644 --- a/public/actions.mob.test.js +++ b/public/actions.mob.test.js @@ -97,9 +97,12 @@ test('can add in-memory name to mob', t => { mob: [makeMobber('One')], name: nameToAdd, externals: { socketEmitter: {} }, + forms: { + mob: {}, + }, }; - const [state, effect] = actions.AddNameToMob(initialState); + const [state, effect] = actions.AddNameToMob(initialState, { name: nameToAdd }); t.is(state.mob.length, initialState.mob.length + 1); t.like(state.mob[1], { name: nameToAdd }); diff --git a/public/actions.prompt.test.js b/public/actions.prompt.test.js.skip similarity index 100% rename from public/actions.prompt.test.js rename to public/actions.prompt.test.js.skip diff --git a/public/actions.timer.test.js b/public/actions.timer.test.js index 53409f1..6d40a74 100644 --- a/public/actions.timer.test.js +++ b/public/actions.timer.test.js @@ -71,9 +71,11 @@ test('can pause the timer', t => { t.deepEqual( effect, - effects.PauseTimer({ - socketEmitter, + effects.apiTimerPause({ + completeToken: undefined, + fetch: undefined, timerDuration: expectedTimerDuration, + timerId: undefined, }), ); }); @@ -102,9 +104,10 @@ test('can resume the timer', t => { t.deepEqual( effect, - effects.StartTimer({ - socketEmitter, - timerDuration: 1000000, + effects.apiTimerStart({ + duration: 1000000, + fetch: undefined, + timerId: undefined, }), ); }); @@ -139,9 +142,10 @@ test('can start the timer', t => { t.deepEqual( effect, - effects.StartTimer({ - socketEmitter, - timerDuration, + effects.apiTimerStart({ + duration: timerDuration, + fetch: undefined, + timerId: undefined, }), ); }); diff --git a/public/components/badge.js b/public/components/badge.js index 61545b6..fca76ec 100644 --- a/public/components/badge.js +++ b/public/components/badge.js @@ -10,7 +10,7 @@ export const badge = (props, children) => 'text-gray-200': true, 'py-1': true, 'px-2': true, - rounded: true, + 'rounded': true, 'bg-green-600': true, }, }, diff --git a/public/components/button.js b/public/components/button.js index 8b4ecb1..9f7050c 100644 --- a/public/components/button.js +++ b/public/components/button.js @@ -9,7 +9,7 @@ export const button = (props, children) => class: { 'py-1': true, 'px-2': true, - uppercase: true, + 'uppercase': true, ...(props.class || {}), }, }, diff --git a/public/components/card.js b/public/components/card.js index e3f8025..f0383e8 100644 --- a/public/components/card.js +++ b/public/components/card.js @@ -1,14 +1,16 @@ import { h } from '/vendor/hyperapp.js'; export const card = (props, children) => { + return h('div', {}, children); + return h( 'div', { ...props, class: { - rounded: true, + 'rounded': true, 'overflow-hidden': true, - shadow: true, + 'shadow': true, 'pt-2': true, 'pb-1': true, ...(props.class || {}), diff --git a/public/components/checkbox.js b/public/components/checkbox.js index 6fc865e..6cded38 100644 --- a/public/components/checkbox.js +++ b/public/components/checkbox.js @@ -1,57 +1,9 @@ import { h } from '../vendor/hyperapp.js'; +import { classConcat } from '../lib/classConcat.js'; -export const checkbox = (props, children) => - h( - 'div', - { - class: { - flex: true, - 'flex-row': true, - 'items-center': true, - 'justify-center': true, - 'justify-between': true, - }, - }, - [ - h('input', { - ...props.inputProps, - id: props.id, - type: 'checkbox', - checked: props.checked, - class: { - 'mr-3': true, - 'sr-only': true, - }, - }), - children && - h( - 'label', - { - for: props.id, - class: { - 'flex-grow': true, - 'leading-tight': true, - flex: true, - 'flex-row': true, - 'items-center': true, - }, - }, - [ - h( - 'span', - { - class: { - 'fa-stack': true, - }, - }, - [ - h('i', { class: 'far fa-circle fa-stack-1x' }), - props.checked && - h('i', { class: 'fas fa-check fa-stack-1x text-green-500' }), - ], - ), - ...(Array.isArray(children) ? children : [children]), - ], - ), - ], - ); +export const checkbox = (props) => + h('input', { + ...props, + type: 'checkbox', + class: classConcat('grow-0 shrink-0 block w-6 h-6', props.class || ''), + }); diff --git a/public/components/column.js b/public/components/column.js new file mode 100644 index 0000000..ff42618 --- /dev/null +++ b/public/components/column.js @@ -0,0 +1,24 @@ +import { h } from '/vendor/hyperapp.js'; + +/* + * sm:col-span-1 + * sm:col-span-2 + * md:col-span-1 + * md:col-span-2 + */ + +export const column = (mobileSpan, breakpointSpans, children) => h( + 'div', + { + class: { + [`col-span-${mobileSpan}`]: mobileSpan > 1, + ...Object.keys(breakpointSpans).reduce((memo, breakpoint) => ({ + ...memo, + [`${breakpoint}:col-span-${breakpointSpans[breakpoint]}`]: true, + }), {}), + }, + }, + children, +); + +column.fixed = (span, children) => column(span, {}, children); diff --git a/public/components/deleteButton.js b/public/components/deleteButton.js index 350cf52..93281ed 100644 --- a/public/components/deleteButton.js +++ b/public/components/deleteButton.js @@ -11,13 +11,6 @@ export const deleteButton = props => 'bg-red-500': true, ...(props.class || {}), }, + innerHTML: '×', }, - [ - h('i', { - class: { - fas: true, - 'fa-times': true, - }, - }), - ], ); diff --git a/public/components/details.js b/public/components/details.js new file mode 100644 index 0000000..3416df6 --- /dev/null +++ b/public/components/details.js @@ -0,0 +1,18 @@ +import { h, text } from '/vendor/hyperapp.js'; +import * as actions from '/actions.js'; + +export const details = (props, children) => h( + 'details', + { + open: props.details[props.which], + ontoggle: (_, e) => { + return [actions.DetailsToggle, { which: props.which, open: e.target.open }]; + }, + }, + [ + h('summary', { + class: 'bg-transparent hover:bg-slate-200 hover:dark:bg-slate-600 text-xs text-slate-400 hover:text-slate-500 hover:dark:text-white px-1 cursor-pointer', + }, text(props.summary)), + ...([].concat(children)), + ], +); diff --git a/public/components/drawer.js b/public/components/drawer.js new file mode 100644 index 0000000..8cbe36c --- /dev/null +++ b/public/components/drawer.js @@ -0,0 +1,58 @@ +import { h, text } from '/vendor/hyperapp.js'; +import { timerSettings } from '/sections/timerSettings.js'; +import * as actions from '/actions.js'; + +export const drawer = (props) => { + return h('aside', { + class: [ + 'absolute', + 'right-0', + 'top-0', + 'h-screen', + 'w-3/4', + 'sm:w-1/2', + 'md:w-1/4', + 'bg-white', + 'dark:bg-gray-700', + 'overflow-y-hidden', + 'shadow-xl', + 'flex', + 'flex-col', + 'justify-start', + 'items-start', + ], + open: true, + }, [ + h('header', { class: 'my-2 px-2 w-full flex justify-between items-start' }, [ + h('h1', { class: 'text-lg' }, text('Timer Configuration')), + + h('button', { + type: 'button', + innerHTML: '×', + onclick: [actions.ToggleDrawer, { showDrawer: false }], + }), + ]), + + h('details', { class: 'm-2' }, [ + h('summary', {}, text('Timer Settings')), + h('p', {}, text('These settings live in local storage, and are your personal preferences')), + timerSettings(props), + ]), + h('details', { class: 'm-2' }, [ + h('summary', {}, text('Local Settings')), + h('p', {}, text('These settings live in local storage, and are your personal preferences')), + ]), + h('details', { class: 'm-2' }, [ + h('summary', {}, text('Shareable QR Code')), + h('img', { + src: props.qrImage ? props.qrImage.src : '', + class: { + 'mb-3': true, + }, + }), + h('p', {}, h('small', {}, text(props.lang.share.mobtimePoweredByWebsockets))), + h('p', {}, h('small', {}, text(props.lang.share.mobtimeCollaborate))), + ]), + + ]); +}; diff --git a/public/components/goal.js b/public/components/goal.js index 6671b14..e469a56 100644 --- a/public/components/goal.js +++ b/public/components/goal.js @@ -1,5 +1,7 @@ import { h, text } from '/vendor/hyperapp.js'; +import { checkbox } from '/components/checkbox.js'; + import * as actions from '/actions.js'; const textWithBreaks = goalText => @@ -8,70 +10,46 @@ const textWithBreaks = goalText => .reduce((result, t) => [...result, text(t), h('br', {})], []) .slice(0, -1); -export const goal = props => - h( +const indentRegex = () => /^[- ]+/; +const shouldIndent = (text) => indentRegex().test(text); +const normalizeText = (text) => text.replace(indentRegex(), ''); + +export const goal = props => { + const indent = shouldIndent(props.text); + const text = normalizeText(props.text); + + return h( 'div', { class: { 'flex': true, - 'flex-row': true, 'items-center': true, 'justify-between': true, 'mb-2': true, 'w-full': true, 'break-words': true, 'truncate': props.truncate, + 'outline': props.highlight, + 'outline-blue-300': true, + 'ml-6': indent, }, }, [ - h('input', { + checkbox({ id: `goal-${props.id}`, - type: 'checkbox', checked: props.completed, + disabled: !props.id, onchange: (_, e) => [ actions.CompleteGoal, { id: props.id, completed: e.target.checked }, ], - class: { - 'sr-only': true, - }, }), - h( - 'button', - { - disabled: props.id === null, - onclick: - props.id !== null - ? () => [ - actions.CompleteGoal, - { id: props.id, completed: !props.completed }, - ] - : undefined, - class: { - 'text-gray-500': props.id === null, - }, - }, - [ - h( - 'span', - { - class: { - 'fa-stack': true, - }, - }, - [ - h('i', { class: 'far fa-circle fa-stack-1x' }), - props.completed && - h('i', { class: 'fas fa-check fa-stack-1x text-green-500' }), - ], - ), - ], - ), h( 'label', { - for: `goal-${props.id}`, + // for: `goal-${props.id}`, class: { + 'ml-2': true, 'pr-1': true, 'flex-grow': true, 'leading-tight': true, @@ -79,9 +57,15 @@ export const goal = props => 'break-words': true, 'truncate': props.truncate, 'block': true, + 'border-b': true, + 'border-dotted': true, + 'border-transparent': true, + 'hover:border-slate-400': props.id, }, + ondblclick: [actions.GoalDoubleClick, { id: props.id }], }, - textWithBreaks(props.text), + textWithBreaks(text || ''), ), ], ); +}; diff --git a/public/components/grid.js b/public/components/grid.js new file mode 100644 index 0000000..583dcb7 --- /dev/null +++ b/public/components/grid.js @@ -0,0 +1,11 @@ +import { h } from '/vendor/hyperapp.js'; +import { classConcat } from '/lib/classConcat.js'; + +export const grid = (props, children) => h( + 'div', + { + ...props, + class: classConcat('grid px-1 md:px-4 grid-cols-2 gap-6', props.class), + }, + children, +); diff --git a/public/components/listButton.js b/public/components/listButton.js index bf6193a..377b7ab 100644 --- a/public/components/listButton.js +++ b/public/components/listButton.js @@ -7,10 +7,11 @@ export const listButton = (props, children) => ...props, class: { 'box-border': true, - 'w-8': true, - 'h-8': true, + 'w-6': true, + 'h-6': true, + 'text-sm': true, 'flex-shrink-0': true, - flex: true, + 'flex': true, 'items-center': true, 'justify-center': true, ...(props.class || {}), diff --git a/public/components/mobber.js b/public/components/mobber.js index 2068c5f..f3b8984 100644 --- a/public/components/mobber.js +++ b/public/components/mobber.js @@ -1,24 +1,26 @@ import { h, text } from '/vendor/hyperapp.js'; - -import { section } from '/components/section.js'; +import * as actions from '/actions.js'; export const mobber = props => h( 'div', { class: { - flex: true, + 'flex': true, 'flex-row': true, 'items-center': true, 'justify-between': true, - 'mb-1': true, + // 'mb-1': true, 'h-full': true, 'w-full': true, - truncate: props.truncate, - 'bg-indigo-50': props.position !== 'mob', - 'dark:bg-indigo-800': props.position !== 'mob', - 'py-2': true, + 'text-black': props.hasPosition, + 'dark:text-white': props.hasPosition, + // 'bg-indigo-50': props.position !== 'mob', + // 'dark:bg-indigo-800': props.position !== 'mob', + 'py-1': true, 'pl-1': true, + 'outline': props.highlight, + 'outline-blue-300': true, }, }, [ @@ -26,18 +28,25 @@ export const mobber = props => 'div', { class: { - truncate: props.truncate, + 'truncate': props.selected, + 'border-b': true, + 'border-dotted': true, + 'border-transparent': true, + 'hover:border-slate-400': props.name, }, + ondblclick: [actions.MobDoubleClick, { id: props.id }], + title: 'Double click to edit', }, [ h( 'div', { class: { - uppercase: true, + 'uppercase': true, 'leading-none': true, 'mb-1': true, 'break-all': true, + 'text-xs': true, }, }, text(props.position), @@ -47,10 +56,12 @@ export const mobber = props => { class: { 'text-gray-500': !props.name, - 'font-bold': props.position !== 'mob', + 'dark:text-gray-600': !props.name, + 'font-lg': true, + 'font-bold': props.hasPosition && props.name, 'leading-none': true, 'break-all': true, - truncate: props.truncate, + 'truncate': props.selected, }, }, text(props.name || 'Empty'), diff --git a/public/components/overlay.js b/public/components/overlay.js index f2f04fb..3cce005 100644 --- a/public/components/overlay.js +++ b/public/components/overlay.js @@ -6,9 +6,9 @@ export const overlay = (props = {}, children) => { ...props, class: { - absolute: true, + 'absolute': true, 'inset-0': true, - flex: true, + 'flex': true, 'items-center': true, 'justify-center': true, 'z-40': true, diff --git a/public/components/overviewHeading.js b/public/components/overviewHeading.js index aa48237..ed73a0f 100644 --- a/public/components/overviewHeading.js +++ b/public/components/overviewHeading.js @@ -10,7 +10,7 @@ export const overviewHeading = (props, children) => 'px-1': true, 'mx-5': true, 'pt-3': true, - flex: true, + 'flex': true, 'flex-row': true, 'items-end': true, 'justify-between': true, diff --git a/public/components/reorderable.js b/public/components/reorderable.js index 03f30b8..0ebf1a1 100644 --- a/public/components/reorderable.js +++ b/public/components/reorderable.js @@ -73,8 +73,9 @@ const dragContainer = (props, children) => 'div', { class: { - 'hidden': true, - 'sm:flex': true, + 'flex': true, + // 'hidden': true, + // 'sm:flex': true, 'h-full': true, 'flex-col': true, 'items-center': true, @@ -96,19 +97,20 @@ const dragContainer = (props, children) => }, ], }, - Array.from({ length: 3 }, () => - h('div', { - class: { - 'border-b': true, - 'border-b-gray-800': !props.disabled, - 'border-b-gray-400': props.disabled, - 'dark:border-b-gray-200': !props.disabled, - 'dark:border-b-gray-400': props.disabled, - 'my-1': true, - 'w-6': true, - }, - }), - ), + h('div', { innerHTML: '⁞⁞⁞' }), + // Array.from({ length: 3 }, (_, index) => + // h('div', { + // class: { + // 'border-b': true, + // 'border-b-gray-800': !props.disabled, + // 'border-b-gray-400': props.disabled, + // 'dark:border-b-gray-200': !props.disabled, + // 'dark:border-b-gray-400': props.disabled, + // 'mt-1': index > 0, + // 'w-1': true, + // }, + // }), + // ), ), children, @@ -126,50 +128,44 @@ const dragContainer = (props, children) => { 'class': { 'text-gray-500': !props.onMoveUp, - 'border-2': true, - 'border-white': true, - 'mr-2': true, + 'mr-1': true, }, 'onclick': props.onMoveUp, 'disabled': !props.onMoveUp, 'aria-label': `Move ${props.type} up`, }, - [h('i', { class: 'fas fa-arrow-up' })], + [h('span', { class: 'text-bold', innerHTML: '↑' })], ), listButton( { 'class': { 'text-gray-500': !props.onMoveDown, - 'border-2': true, - 'border-white': true, - 'mr-2': true, + 'mr-1': true, }, 'onclick': props.onMoveDown, 'disabled': !props.onMoveDown, 'aria-label': `Move ${props.type} down`, }, - [h('i', { class: 'fas fa-arrow-down' })], + [h('span', { class: 'text-bold', innerHTML: '↓' })], ), props.onEdit && listButton( { class: { - 'border-2': true, - 'border-white': true, 'mr-2': true, }, onclick: props.onEdit, }, - [h('i', { class: 'fas fa-pencil-alt' })], + [h('span', { class: 'text-bold', innerHTML: '✎' })], ), props.onDelete && deleteButton({ onclick: props.onDelete, class: { - 'mr-2': true, + 'mr-1': true, }, }), ], @@ -182,14 +178,11 @@ const dragContainer = (props, children) => class: { 'text-indigo-600': props.expandActions, 'bg-white': props.expandActions, - 'border-2': true, - 'border-transparent': !props.expandActions, - 'border-white': props.expandActions, - 'mr-2': true, + 'mr-1': true, }, onclick: props.onExpand, }, - [h('i', { class: 'fas fa-ellipsis-h' })], + [h('span', { class: 'text-bold', innerHTML: '⋮' })], ), ...(props.isDragging @@ -222,9 +215,9 @@ const draggingContainer = (props, child) => 'duration-75': true, 'ease-in-out': true, 'pointer-events-none': true, - 'border': true, - 'border-green-600': true, - 'rounded': true, + // 'border': true, + // 'border-green-600': true, + // 'rounded': true, }, }, child, diff --git a/public/components/section.js b/public/components/section.js index eb67148..00ed142 100644 --- a/public/components/section.js +++ b/public/components/section.js @@ -1,8 +1,8 @@ import { h } from '/vendor/hyperapp.js'; export const classes = { - 'pt-5': true, - 'px-4': true, + 'mt-5': true, + // 'mx-4': true, }; export const section = (props, children) => diff --git a/public/components/tab.js b/public/components/tab.js index 2f5e981..b96350a 100644 --- a/public/components/tab.js +++ b/public/components/tab.js @@ -16,7 +16,7 @@ export const tab = (props, children) => 'py-1': true, 'px-3': true, 'flex-grow': true, - flex: true, + 'flex': true, 'flex-row': true, 'items-center': true, 'justify-between': true, diff --git a/public/effects.js b/public/effects.js index e16bd5d..7dbd27a 100644 --- a/public/effects.js +++ b/public/effects.js @@ -13,7 +13,7 @@ const sendMessage = (socketEmitter, type, json = {}) => { ); }; -export const PreloadImage = fx(function PreloadImageFX( +export const PreloadImage = fx(function PreloadImageFX ( dispatch, { src, onLoad }, ) { @@ -31,49 +31,49 @@ export const PreloadImage = fx(function PreloadImageFX( img.src = src; }); -export const UpdateSettings = fx(function UpdateSettingsFX( +export const UpdateSettings = fx(function UpdateSettingsFX ( _dispatch, { socketEmitter, settings }, ) { return sendMessage(socketEmitter, 'settings:update', { settings }); }); -export const StartTimer = fx(function StartTimerFX( +export const StartTimer = fx(function StartTimerFX ( _dispatch, { socketEmitter, timerDuration }, ) { return sendMessage(socketEmitter, 'timer:start', { timerDuration }); }); -export const PauseTimer = fx(function StartTimerFX( +export const PauseTimer = fx(function StartTimerFX ( _dispatch, { socketEmitter, timerDuration }, ) { return sendMessage(socketEmitter, 'timer:pause', { timerDuration }); }); -export const CompleteTimer = fx(function CompleteTimerFX( +export const CompleteTimer = fx(function CompleteTimerFX ( _dispatch, { socketEmitter }, ) { return sendMessage(socketEmitter, 'timer:complete'); }); -export const UpdateGoals = fx(function UpdateGoalsFX( +export const UpdateGoals = fx(function UpdateGoalsFX ( _dispatch, { socketEmitter, goals }, ) { sendMessage(socketEmitter, 'goals:update', { goals }); }); -export const UpdateMob = fx(function UpdateMobFX( +export const UpdateMob = fx(function UpdateMobFX ( _dispatch, { socketEmitter, mob }, ) { sendMessage(socketEmitter, 'mob:update', { mob }); }); -export const NotificationPermission = fx(function NotificationPermissionFX( +export const NotificationPermission = fx(function NotificationPermissionFX ( dispatch, { UpdateNotificationPermissions, Notification, documentElement }, ) { @@ -97,7 +97,7 @@ export const NotificationPermission = fx(function NotificationPermissionFX( }); }); -function PlaySoundFX(_dispatch, { sound, documentElement }) { +function PlaySoundFX (_dispatch, { sound, documentElement }) { if (sound && documentElement) { const timerComplete = documentElement.querySelector('#timer-complete'); timerComplete.play(); @@ -105,7 +105,7 @@ function PlaySoundFX(_dispatch, { sound, documentElement }) { } export const PlaySound = fx(PlaySoundFX); -export const Notify = fx(function NotifyFX( +export const Notify = fx(function NotifyFX ( _dispatch, { title, @@ -120,13 +120,13 @@ export const Notify = fx(function NotifyFX( // eslint-disable-next-line no-new new Notification(title, { body: text, - vibrate: [100, 100, 100], + silent: !sound, }); } PlaySoundFX(_dispatch, { sound, documentElement }); }); -export const UpdateTitleWithTime = fx(function UpdateTitleWithTimeFX( +export const UpdateTitleWithTime = fx(function UpdateTitleWithTimeFX ( _dispatch, { remainingTime, documentElement }, ) { @@ -135,25 +135,33 @@ export const UpdateTitleWithTime = fx(function UpdateTitleWithTimeFX( remainingTime > 0 ? `${formatTime(remainingTime)} - mobtime` : 'mobtime'; }); -export const andThen = fx(function andThenFX(dispatch, { action, props, delay }) { +export const andThen = fx(function andThenFX (dispatch, { action, props, delay, ...otherProps }) { + if (!action) { + console.error('Unable to chain action using andThen', { action, props, delay, otherProps }); + return; + } setTimeout(() => { dispatch(action, props); }, delay || 0); }); -export const checkSettings = fx(function CheckSettingsFX( +export const checkSettings = fx(function CheckSettingsFX ( dispatch, - { storage, onLocalSoundEnabled, onDarkEnabled }, + { storage, onAllowSound, onLocalSoundEnabled, onDarkEnabled }, ) { let localSettings = storage.getItem('settings'); if (!localSettings) return; localSettings = JSON.parse(localSettings); + + dispatch(onAllowSound, localSettings.allowSound || false); + if (localSettings.allowSound && onLocalSoundEnabled) { dispatch(onLocalSoundEnabled, { - sound: localSettings.sound || '/audio/horn.wav', + sound: localSettings.sound || '/audio/ding.wav', }); } + if (localSettings.dark && onDarkEnabled) { dispatch(onDarkEnabled, { dark: localSettings.dark, @@ -161,7 +169,7 @@ export const checkSettings = fx(function CheckSettingsFX( } }); -export const saveSettings = fx(function SaveSettingsFX( +export const saveSettings = fx(function SaveSettingsFX ( _dispatch, { storage, data }, ) { @@ -178,7 +186,7 @@ export const saveSettings = fx(function SaveSettingsFX( storage.setItem('settings', JSON.stringify(localSettings)); }); -export const saveSound = fx(function SaveSoundFX( +export const saveSound = fx(function SaveSoundFX ( _dispatch, { storage, allowSound, sound }, ) { @@ -196,11 +204,10 @@ export const saveSound = fx(function SaveSoundFX( storage.setItem('settings', JSON.stringify(localSettings)); }); -export const toggleDarkMode = fx(function ToggleDarkMode( +export const toggleDarkMode = fx(function ToggleDarkMode ( _dispatch, { documentElement, dark }, ) { - console.log('toggleDarkMode', documentElement, dark); if (typeof dark !== 'boolean') { console.log('dark not set, skipping', dark); return; @@ -214,7 +221,7 @@ export const toggleDarkMode = fx(function ToggleDarkMode( } }); -export const removeQueryParameters = fx(function RemoveQueryParametersFX( +export const removeQueryParameters = fx(function RemoveQueryParametersFX ( _dispatch, { location, history, documentElement }, ) { @@ -231,3 +238,59 @@ export const removeQueryParameters = fx(function RemoveQueryParametersFX( [location.toString().split('?')[0], params].filter(Boolean).join('?'), ); }); + +const timerApi = (fetchFn, timerId, endpoint, body = {}) => fetchFn( + `/${timerId}/timer${endpoint}`, + { + method: 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }, +); + +export const apiTimerStart = fx(function ApiTimerStart ( + _dispatch, + { timerId, duration, fetch: fetchFn }, +) { + timerApi(fetchFn, timerId, '/start', { duration }) + .catch((err) => { + console.error('Unable to start timer', { timerId }, err); + }); +}); + +export const apiTimerPause = fx(function ApiTimerPause ( + _dispatch, + { timerId, completeToken, timerDuration, fetch: fetchFn }, +) { + timerApi(fetchFn, timerId, '/pause', { token: completeToken, duration: timerDuration }) + .catch((err) => { + console.error('Unable to start timer', { timerId }, err); + }); +}); + +export const apiTimerComplete = fx(function CompleteTimer ( + dispatch, + { timerId, completeToken, fetch: fetchFn, onSuccess }, +) { + timerApi(fetchFn, timerId, '/complete', { token: completeToken }) + .then((response) => { + return response.ok && onSuccess + ? dispatch(onSuccess.action, onSuccess.props) + : null; + }) + .catch((err) => { + console.error('Unable to complete timer', { timerId, completeToken }, err); + }); +}); + +export const apiUpdateSettings = fx(function UpdateTimer ( + _dispatch, + { timerId, settings, fetch: fetchFn }, +) { + timerApi(fetchFn, timerId, '/settings', { settings }) + .catch((err) => { + console.error('Unable to update timer settings', { timerId, settings }, err); + }); +}); diff --git a/public/effects.test.js b/public/effects.test.js index 975f301..fb06508 100644 --- a/public/effects.test.js +++ b/public/effects.test.js @@ -165,7 +165,7 @@ test('can request notification permission, and handle exception', async t => { }); test('can create a notification', t => { - const Notification = sinon.fake(function _Notification(title, options) { + const Notification = sinon.fake(function _Notification (title, options) { this.title = title; this.options = options; return this; @@ -192,7 +192,7 @@ test('can create a notification', t => { }); test('can skip a notification when notification is false', t => { - const Notification = sinon.fake(function _Notification(title, options) { + const Notification = sinon.fake(function _Notification (title, options) { this.title = title; this.options = options; return this; diff --git a/public/i18n/af-ZA.js b/public/i18n/af-ZA.js index 4f87319..fa93835 100644 --- a/public/i18n/af-ZA.js +++ b/public/i18n/af-ZA.js @@ -4,13 +4,13 @@ const nomenclature = { create: 'Voeg by', edit: 'Wysig', product: 'mobtime', -} +}; const properCase = text => text .split(' ') .map(word => word.slice(0, 1).toUpperCase() + word.slice(1)) - .join(' ') + .join(' '); export const af_ZA = { header: { @@ -94,4 +94,4 @@ export const af_ZA = { reconnect: 'Herkonnekteer', }, }, -} +}; diff --git a/public/i18n/uk-UA.js b/public/i18n/uk-UA.js index fbc0e12..c67f721 100644 --- a/public/i18n/uk-UA.js +++ b/public/i18n/uk-UA.js @@ -62,7 +62,7 @@ export const uk_UA = { saved: 'Збережено', sharedTimerSettings: 'Налаштування таймеру', turnDurationInMinutes: 'Довжина ротації (у хвилинах)', - mobRolesOrder: `Ролі/Порядок учасників`, + mobRolesOrder: 'Ролі/Порядок учасників', positionHelpText: 'Одна або більше ролей розділених комами', localSettings: 'Локальні налаштування', enableTimerSounds: 'Увімкнути звуки таймеру', diff --git a/public/images/unite.eu.png b/public/images/unite.eu.png new file mode 100644 index 0000000..115f92e Binary files /dev/null and b/public/images/unite.eu.png differ diff --git a/public/index.html b/public/index.html index 97c1dea..b9fca8b 100644 --- a/public/index.html +++ b/public/index.html @@ -154,9 +154,11 @@

Some humans and groups who use Mobtime< - - Your Logo Here + + + Unite - Connecting buyrs and sellers for mutual benefit + Your Logo Here diff --git a/public/lib/classConcat.js b/public/lib/classConcat.js new file mode 100644 index 0000000..e07baae --- /dev/null +++ b/public/lib/classConcat.js @@ -0,0 +1,15 @@ +const normalize = (classes) => { + if (typeof classes === 'string') { + return classes; + } else if (Array.isArray) { + return classes.filter(Boolean).map(normalize).join(' '); + } else if (!classes) { + return ''; + } + return normalize( + Object.keys(classes) + .reduce((memo, key) => memo.concat(classes[key] ? key : []), []), + ); +}; + +export const classConcat = (...definitions) => normalize(definitions); diff --git a/public/lib/form.js b/public/lib/form.js new file mode 100644 index 0000000..520d069 --- /dev/null +++ b/public/lib/form.js @@ -0,0 +1,4 @@ +export const formData = (formElement) => { + const formData = new FormData(formElement); + return Array.from(formData.keys()).reduce((obj, key) => ({ ...obj, [key]: formData.get(key) }), {}); +}; diff --git a/public/lib/formatTime.js b/public/lib/formatTime.js new file mode 100644 index 0000000..bf096ef --- /dev/null +++ b/public/lib/formatTime.js @@ -0,0 +1,21 @@ +export const formatTime = milliseconds => { + console.log('formatTime', milliseconds); + if (!milliseconds) { + return '00:00'; + } + const minutes = Math.floor(milliseconds / 60000); + const remainingMilliseconds = milliseconds - minutes * 60000; + const seconds = Math.floor(remainingMilliseconds / 1000); + + return [minutes, seconds].map(t => `${t}`.padStart(2, '0')).join(':'); +}; + +export const formattedTimeToMilliseconds = (formatted) => { + if (!formatted) return 0; + + const [minutes, seconds] = formatted + .split(':') + .map((v) => parseInt(v)); + + return (seconds + (minutes * 60)) * 1000; +}; diff --git a/public/lib/formatTime.test.js b/public/lib/formatTime.test.js new file mode 100644 index 0000000..906dc00 --- /dev/null +++ b/public/lib/formatTime.test.js @@ -0,0 +1,22 @@ +import test from 'ava'; +import { formatTime } from './formatTime.js'; + +test('displays 00:00 with empty milliseconds', t => { + const expectedTime = '00:00'; + t.is(formatTime(), expectedTime); +}); + +test('displays under-a-minute time', t => { + const expectedTime = '00:59'; + t.is(formatTime(59000), expectedTime); +}); + +test('displays under 10-minute time', t => { + const expectedTime = '09:00'; + t.is(formatTime(9 * 60 * 1000), expectedTime); +}); + +test('displays over 10-minute time', t => { + const expectedTime = '99:00'; + t.is(formatTime(99 * 60 * 1000), expectedTime); +}); diff --git a/public/sections/addGoal.js b/public/sections/addGoal.js deleted file mode 100644 index f9adf06..0000000 --- a/public/sections/addGoal.js +++ /dev/null @@ -1,97 +0,0 @@ -import { h, text } from '/vendor/hyperapp.js'; - -import { section } from '/components/section.js'; -import { input, textarea } from '/components/input.js'; -import { button } from '/components/button.js'; -import { checkbox } from '/components/checkbox.js'; - -import * as actions from '/actions.js'; - -export const addGoal = props => - section({}, [ - h( - 'form', - { - action: '#', - method: 'get', - onsubmit: (_, e) => { - e.preventDefault(); - return [actions.AddGoals, props.goal]; - }, - class: { - 'flex': true, - 'flex-col': true, - 'items-center': true, - 'justify-start': true, - 'w-full': true, - }, - autocomplete: 'off', - }, - [ - !props.addMultiple && - input({ - value: props.goal, - oninput: (_, e) => [actions.UpdateGoalText, e.target.value], - placeholder: props.lang.goals.addSingle, - class: { - 'hover:border-indigo-300': true, - 'hover:border-b-solid': true, - 'w-full': true, - }, - }), - - props.addMultiple && - textarea({ - onchange: (_, e) => [actions.UpdateGoalText, e.target.value], - value: props.goal, - placeholder: props.lang.goals.addMultiline, - class: { - 'w-full': true, - }, - }), - - h( - 'div', - { - class: { - 'flex': true, - 'items-center': true, - 'justify-between': true, - 'pt-2': true, - 'w-full': true, - }, - }, - [ - checkbox( - { - id: 'goals-allow-multiple', - checked: props.addMultiple, - inputProps: { - onchange: (_, e) => { - e.preventDefault(); - return [actions.SetAddMultiple, e.target.checked]; - }, - }, - }, - text(props.lang.goals.addMultipleGoals), - ), - - button( - { - type: 'submit', - class: { - 'bg-green-600': true, - 'text-white': true, - 'whitespace-no-wrap': true, - }, - }, - [ - h('i', { class: 'fas fa-plus mr-3' }), - text(props.lang.goals.add), - ], - ), - ], - ), - ], - ), - ]); diff --git a/public/sections/addParticipant.js b/public/sections/addParticipant.js index d2cc377..766cd2f 100644 --- a/public/sections/addParticipant.js +++ b/public/sections/addParticipant.js @@ -53,8 +53,8 @@ export const addParticipant = props => { type: 'submit', class: { - 'bg-green-600': true, - 'text-white': true, + // 'bg-green-600': true, + 'dark:text-white': true, 'flex-shrink': true, 'whitespace-no-wrap': true, }, diff --git a/public/sections/goalList.js b/public/sections/goalList.js index 9f6a622..ba556c9 100644 --- a/public/sections/goalList.js +++ b/public/sections/goalList.js @@ -36,6 +36,7 @@ export const goalList = props => { renderItem: item => goal({ ...item, + highlight: props.forms.goal.id === item.id, truncate: getReorderableId(item) === props.expandedReorderable, }), drag: props.drag, diff --git a/public/sections/header.js b/public/sections/header.js index ad8427b..22905f3 100644 --- a/public/sections/header.js +++ b/public/sections/header.js @@ -10,11 +10,15 @@ export const header = state => 'flex-row': true, 'items-center': true, 'justify-start': true, + 'w-full': true, + 'text-black': true, + 'dark:text-white': true, }, }, [ h('div', { - class: 'fas fa-clock text-4xl mr-3', + innerHTML: '🕐', + class: 'text-4xl mr-3', }), h( 'div', @@ -23,9 +27,11 @@ export const header = state => 'uppercase': true, 'tracker-widest': true, 'text-2xl': true, + 'flex-grow': true, }, }, text(state.lang.header.product), ), + ], ); diff --git a/public/sections/mobActions.js b/public/sections/mobActions.js deleted file mode 100644 index ae9d9ca..0000000 --- a/public/sections/mobActions.js +++ /dev/null @@ -1,51 +0,0 @@ -import { text, h } from '/vendor/hyperapp.js'; - -import { section } from '/components/section.js'; -import { button } from '/components/button.js'; - -import * as actions from '/actions.js'; - -export const mobActions = props => - section( - { - class: { - 'flex': true, - 'flex-row': true, - 'items-center': true, - 'justify-between': true, - }, - }, - [ - button( - { - class: { - 'bg-green-600': true, - 'text-white': true, - 'flex-grow': true, - 'mr-1': true, - }, - onclick: actions.CycleMob, - }, - [ - h('x-icon', { class: 'inline fas fa-sync-alt mr-1' }), - text(props.lang.mob.rotate), - ], - ), - - button( - { - class: { - 'bg-green-600': true, - 'text-white': true, - 'flex-grow': true, - 'ml-1': true, - }, - onclick: actions.ShuffleMob, - }, - [ - h('x-icon', { class: 'inline fas fa-random mr-1' }), - text(props.lang.mob.randomize), - ], - ), - ], - ); diff --git a/public/sections/mobParticipants.js b/public/sections/mobParticipants.js index 1a5073e..a28d85f 100644 --- a/public/sections/mobParticipants.js +++ b/public/sections/mobParticipants.js @@ -2,7 +2,6 @@ import { h } from '/vendor/hyperapp.js'; import * as actions from '/actions.js'; -import { section } from '/components/section.js'; import { reorderable } from '/components/reorderable.js'; import { mobber } from '/components/mobber.js'; @@ -18,31 +17,31 @@ export const mobParticipants = props => { const items = Array.from({ length }, (_, index) => ({ ...(props.mob[index] || {}), + highlight: props.forms.mob.id === props.mob[index]?.id, disabled: index >= props.mob.length, position: mobOrder[index] || props.lang.mob.fallback, + hasPosition: !!mobOrder[index], })); - return section({}, [ - h( - 'div', - {}, - reorderable({ - dragType: 'mob', - expandedReorderable: props.expandedReorderable, - items, - disabled: props.mob.length === 0, - renderItem: item => - mobber({ - ...item, - truncate: getReorderableId(item) === props.expandedReorderable, - }), - drag: props.drag, - disabled: props.overview, - getReorderableId, - onMove: props.overview ? undefined : actions.MoveMob, - onEdit: props.overview ? undefined : actions.RenameUserPrompt, - onDelete: props.overview ? undefined : actions.RemoveFromMob, - }), - ), - ]); + return h( + 'div', + {}, + reorderable({ + dragType: 'mob', + expandedReorderable: props.expandedReorderable, + items, + disabled: props.mob.length === 0 || props.overview, + renderItem: item => + mobber({ + ...item, + truncate: getReorderableId(item) === props.expandedReorderable, + selected: getReorderableId(item) === props.expandedReorderable, + }), + drag: props.drag, + getReorderableId, + onMove: props.overview ? undefined : actions.MoveMob, + onEdit: props.overview ? undefined : actions.RenameUserPrompt, + onDelete: props.overview ? undefined : actions.RemoveFromMob, + }), + ); }; diff --git a/public/sections/summary.js b/public/sections/summary.js new file mode 100644 index 0000000..aafd457 --- /dev/null +++ b/public/sections/summary.js @@ -0,0 +1,123 @@ +import { h, text } from '/vendor/hyperapp.js'; + +import * as actions from '/actions.js'; + +const subGoalRegex = () => /^[- \t]+/; + +const isSubGoal = (g) => { + if (!g) return false; + return subGoalRegex().test(g.text); +}; + +export const summary = (props) => { + const index = props.goals.findIndex((g) => !g.completed); + const incomplete = { + goal: null, + subGoal: null, + }; + if (index >= 0) { + incomplete.goal = props.goals[index]; + if (!isSubGoal(incomplete.goal)) { + for ( + let i = index + 1; + i < props.goals.length && isSubGoal(props.goals[i]) && + !incomplete.subGoal; + i++ + ) { + const g = props.goals[i]; + if (!g.completed) { + incomplete.subGoal = g; + } + } + } + } + + return h('div', { + // class: "flex items-center justify-start", + }, [ + h('ol', {}, [ + ...props.settings.mobOrder.split(',').map((position, index) => { + return h('li', { class: 'mb-2', 'data-mobposition': position }, [ + h( + 'div', + { class: 'text-xs leading-none uppercase text-slate-600' }, + text(position), + ), + h('div', {}, text(props.mob[index]?.name || 'Empty')), + ]); + }), + ]), + incomplete.goal && h('div', { class: 'mb-2' }, [ + h('hr', { class: 'mb-2' }), + h( + 'div', + { class: 'text-xs leading-none uppercase text-slate-600' }, + text('Current Goal'), + ), + h('div', { class: '' }, [ + text(incomplete.goal.text), + incomplete.subGoal && + h( + 'div', + { class: 'ml-2' }, + text(incomplete.subGoal.text.replace(subGoalRegex(), '')), + ), + ]), + ]), + h('div', {}, [ + h('hr', { class: 'mb-2' }), + h( + 'div', + { class: 'text-xs uppercase text-slate-600' }, + text('Turn Controls'), + ), + h('div', { class: 'grid grid-cols-3' }, [ + h('button', { + 'data-timercontrol': 'start', + type: 'button', + class: [ + 'mr-1 px-2 py-1 border border-slate-700', + props.timerStartedAt && + 'bg-slate-200 text-black cursor-not-allowed', + ].filter(Boolean).join(' '), + disabled: props.timerStartedAt, + onclick: props.timerDuration + ? [actions.ResumeTimer, {}] + : [actions.StartTimer, { + timerStartedAt: Date.now(), + timerDuration: props.settings.duration, + }], + }, text('Start')), + h('button', { + 'data-timercontrol': 'pause', + type: 'button', + class: [ + 'mr-1 px-2 py-1 border border-slate-700', + !props.timerStartedAt && + 'bg-slate-200 text-black cursor-not-allowed', + ].filter(Boolean).join(' '), + disabled: !props.timerStartedAt, + onclick: [actions.PauseTimer, undefined], + }, text('Pause')), + h('button', { + 'data-timercontrol': 'skip', + type: 'button', + class: [ + 'mr-1 px-2 py-1 border border-slate-700', + !props.timerDuration && + 'bg-slate-200 text-black cursor-not-allowed', + ].join(' '), + disabled: !props.timerDuration, + onclick: [ + actions.Completed, + { + isEndOfTurn: true, + documentElement: document, + Notification: window.Notification, + }, + ], + }, text('Skip')), + ]), + ]), + ]); +}; diff --git a/public/sections/timeRemaining.js b/public/sections/timeRemaining.js index c5eae68..03c4441 100644 --- a/public/sections/timeRemaining.js +++ b/public/sections/timeRemaining.js @@ -1,42 +1,76 @@ import { h, text } from '/vendor/hyperapp.js'; - import { section } from '/components/section.js'; -import { button } from '/components/button.js'; -import { deleteButton } from '/components/deleteButton.js'; import { calculateTimeRemaining } from '/lib/calculateTimeRemaining.js'; import timerRemainingDisplay from '/formatTime.js'; -import * as actions from '/actions.js'; export const timeRemaining = props => { const isPaused = props.timerStartedAt === null; const remainingTime = calculateTimeRemaining(props); - return section({}, [ - h( - 'h2', - { - class: { - 'text-sm': true, - 'font-bold': true, - 'uppercase': true, - }, - }, - text(props.lang.timeRemaining.remainingTime), - ), + const elapsed = props.timerStartedAt + ? props.currentTime - props.timerStartedAt + : 0; + const percent = elapsed > 0 && props.timerDuration > 0 + ? (elapsed / props.timerDuration) * 4 + : 0; + const pulse = (Math.floor(elapsed / 1000) % 2) === 0; + + // border-t-slate-500 + // border-r-slate-500 + // border-b-slate-500 + // border-l-slate-500 + + return section({ + // class: { 'md:col-span-2': true }, + }, [ h( 'div', { class: { 'flex': true, - 'flex-row': true, + 'flex-col': true, 'items-center': true, - 'justify-between': true, + 'justify-center': true, + 'outline': true, + 'outline-indigo-400': true, + 'border-t-4': percent > 0, + 'border-t-transparent': percent > 0 && pulse, + 'border-t-blue-300': percent > 0 && !pulse, + 'border-r-4': percent > 1, + 'border-r-transparent': percent > 0 && pulse, + 'border-r-blue-300': percent > 0 && !pulse, + 'border-b-4': percent > 2, + 'border-b-transparent': percent > 0 && pulse, + 'border-b-blue-300': percent > 0 && !pulse, + 'border-l-4': percent > 3, + 'border-l-transparent': percent > 0 && pulse, + 'border-l-blue-300': percent > 0 && !pulse, + 'w-52': true, + 'h-52': true, + 'rounded-full': true, + // 'mx-auto': true, + 'mb-8': true, + 'transition-all': true, + 'duration-1000': true, + 'ease-in-out': true, }, }, [ + h( + 'h2', + { + class: { + 'text-sm': true, + 'font-bold': true, + 'uppercase': true, + }, + }, + text(props.lang.timeRemaining.remainingTime), + ), + h( 'h3', { @@ -62,77 +96,93 @@ export const timeRemaining = props => { }, text(timerRemainingDisplay(remainingTime)), ), - remainingTime > 0 && - deleteButton({ - size: '24px', - onclick: () => [ - actions.Completed, - { - isEndOfTurn: false, - documentElement: document, - Notification: window.Notification, - }, - ], - }), + // remainingTime > 0 && + // deleteButton({ + // size: '24px', + // onclick: () => [ + // actions.Completed, + // { + // isEndOfTurn: false, + // documentElement: document, + // Notification: window.Notification, + // }, + // ], + // }), ], ), + ], + ), - !props.timerDuration && - button( - { - class: { - 'bg-green-600': true, - 'text-white': true, - }, - onclick: () => [ - actions.StartTimer, - { - timerStartedAt: Date.now(), - timerDuration: props.settings.duration, - }, - ], - }, - [ - h('i', { - class: { - 'fas': true, - 'fa-play': true, - 'mr-4': true, - }, - }), - text(props.lang.timeRemaining.startTurn), - ], - ), + h( + 'div', + { + class: { + 'flex': true, + 'flex-col': true, + 'items-center': true, + 'justify-center': true, + }, + }, + // [ - !!props.timerDuration && - button( - { - class: { - 'bg-white': true, - 'text-green-600': true, - }, - disabled: !props.timerDuration, - onclick: isPaused - ? [actions.ResumeTimer, undefined] - : [actions.PauseTimer, undefined], - }, - [ - h('i', { - class: { - 'fas': true, - 'fa-pause': !isPaused, - 'fa-play': isPaused, - 'mr-4': true, - }, - }), - text( - isPaused - ? props.lang.timeRemaining.resume - : props.lang.timeRemaining.pause, - ), - ], - ), - ], + // // + + // !props.timerDuration && + // button( + // { + // class: { + // 'bg-green-600': true, + // 'text-white': true, + // }, + // onclick: () => [ + // actions.StartTimer, + // { + // timerStartedAt: Date.now(), + // timerDuration: props.settings.duration, + // }, + // ], + // }, + // [ + // h('i', { + // class: { + // 'fas': true, + // 'fa-play': true, + // 'mr-4': true, + // }, + // }), + // text(props.lang.timeRemaining.startTurn), + // ], + // ), + + // !!props.timerDuration && + // button( + // { + // class: { + // 'bg-white': true, + // 'text-green-600': true, + // }, + // disabled: !props.timerDuration, + // onclick: isPaused + // ? [actions.ResumeTimer, undefined] + // : [actions.PauseTimer, undefined], + // }, + // [ + // h('i', { + // class: { + // 'fas': true, + // 'fa-pause': !isPaused, + // 'fa-play': isPaused, + // 'mr-4': true, + // }, + // }), + // text( + // isPaused + // ? props.lang.timeRemaining.resume + // : props.lang.timeRemaining.pause, + // ), + // ], + // ), + // ], ), ]); }; diff --git a/public/sections/timeSection.js b/public/sections/timeSection.js new file mode 100644 index 0000000..f917f6d --- /dev/null +++ b/public/sections/timeSection.js @@ -0,0 +1,22 @@ +import { h, text } from '/vendor/hyperapp.js'; +import { timeRemaining } from '/sections/timeRemaining.js'; + +export const timeSection = (props) => { + return h('div', { + class: 'md:col-span-2 grid grid-cols-2', + }, [ + timeRemaining(props), + h('div', { + class: 'flex items-center justify-start', + }, [ + h('ol', {}, [ + ...props.settings.mobOrder.split(',').map((position, index) => { + return h('li', {}, [ + h('div', {}, text(position)), + h('div', {}, text(props.mob[index]?.name || 'Empty')), + ]); + }), + ]), + ]), + ]); +}; diff --git a/public/sections/timerSettings.js b/public/sections/timerSettings.js new file mode 100644 index 0000000..64f647f --- /dev/null +++ b/public/sections/timerSettings.js @@ -0,0 +1,135 @@ +import * as actions from '/actions.js'; +import { button } from '/components/button.js'; +import { overviewHeading } from '/components/overviewHeading.js'; +import { section } from '/components/section.js'; +import { input } from '/components/input.js'; +import { h, text } from '/vendor/hyperapp.js'; + +const isNumber = value => Number(value) == value; // eslint-disable-line eqeqeq + +const toMinutes = value => { + if (value === '') return value; + return isNumber(value) ? parseInt(value / 60000, 10) : value; +}; + +const toSeconds = value => { + if (value === '') return value; + return isNumber(value) ? value * 60000 : value; +}; + +const value = (key, { pendingSettings, settings }) => + key in pendingSettings ? pendingSettings[key] : settings[key]; + +export const timerSettings = props => + h('div', {}, [ + overviewHeading( + { + rightAction: + Object.keys(props.pendingSettings).length === 0 + ? h('div', {}, [ + text(props.lang.settings.saved), + h('i', { class: 'fas fa-check text-green-500' }), + ]) + : h('div', {}, [ + button( + { + class: { + 'bg-indigo-500': true, + 'hover:bg-indigo-400': true, + 'text-white': true, + 'mr-1': true, + }, + onclick: actions.PendingSettingsReset, + }, + text(props.lang.settings.cancel), + ), + button( + { + type: 'button', + class: { + 'bg-green-600': true, + 'hover:bg-green-500': true, + 'text-white': true, + }, + disable: Object.keys(props.pendingSettings).length === 0, + onclick: actions.UpdateSettings, + }, + text(props.lang.settings.save), + ), + ]), + }, + text(props.lang.settings.sharedTimerSettings), + ), + + section( + { + class: { + 'grid': true, + 'grid-cols-2': true, + 'gap-2': true, + }, + }, + [ + h('div', {}, text(props.lang.settings.turnDurationInMinutes)), + input({ + name: 'setLength', + maxlength: 2, + pattern: '[1-9]{0,2}', + value: toMinutes(value('duration', props)), + oninput: (_, e) => { + e.preventDefault(); + return [ + actions.PendingSettingsSet, + { + key: 'duration', + value: toSeconds(e.target.value), + }, + ]; + }, + + class: { + 'hover:border-indigo-300': true, + 'hover:border-b-solid': true, + 'w-full': true, + }, + }), + + h('div', {}, text(props.lang.settings.mobRolesOrder)), + h( + 'div', + { + class: { + 'w-full': true, + }, + }, + [ + input({ + name: 'mobOrder', + pattern: '^[^,].+[^,]$', + value: value('mobOrder', props), + oninput: (_, e) => [ + actions.PendingSettingsSet, + { + key: 'mobOrder', + value: e.target.value, + }, + ], + + class: { + 'hover:border-indigo-300': true, + 'hover:border-b-solid': true, + 'w-full': true, + }, + }), + h( + 'small', + { + class: 'text-sm', + }, + text(props.lang.settings.positionHelpText), + ), + ], + ), + ], + ), + ]); diff --git a/public/sections/toasts.js b/public/sections/toasts.js index afc452b..244ab59 100644 --- a/public/sections/toasts.js +++ b/public/sections/toasts.js @@ -28,7 +28,7 @@ export const toast = ({ id, title, body, buttons }) => 'mt-2': true, 'pt-2': true, 'border-t': true, - flex: true, + 'flex': true, 'align-center': true, 'justify-start': true, }, @@ -93,7 +93,7 @@ export const toasts = props => 'absolute top-0 right-0 sm:right-20 pt-2 mx-2 flex flex-col align-center justify-end', style: { 'max-height': '50vh', - width: '300px', + 'width': '300px', }, }, [...props.toasts.map(toast)], diff --git a/public/subscriptions.js b/public/subscriptions.js index c56f6b0..5147289 100644 --- a/public/subscriptions.js +++ b/public/subscriptions.js @@ -21,11 +21,7 @@ const TimerFX = (dispatch, { timerStartedAt, timerDuration, actions }) => { if (elapsed >= timerDuration) { cleanup(); - dispatch(actions.Completed, { - isEndOfTurn: true, - documentElement: document, - Notification: window.Notification, - }); + dispatch(actions.TimeComplete, {}); return; } @@ -44,7 +40,7 @@ const WebsocketFX = (dispatch, { timerId, externals, actions }) => { const protocol = externals.location.protocol === 'https:' ? 'wss' : 'ws'; const websocketAddress = `${protocol}://${externals.location.hostname}:${externals.location.port}/${timerId}`; - const socket = new WebSocket(websocketAddress); + let socket = new WebSocket(websocketAddress); let hasError = false; socket.addEventListener('message', event => { @@ -80,6 +76,7 @@ const WebsocketFX = (dispatch, { timerId, externals, actions }) => { return () => { cancel(); socket.close(); + socket = null; }; }; export const Websocket = props => [WebsocketFX, props]; @@ -117,3 +114,20 @@ const DragAndDropFX = (dispatch, props) => { }; }; export const DragAndDrop = props => [DragAndDropFX, props]; + +const PageInteractionFx = (dispatch, props) => { + const onInteraction = () => { + dispatch(props.onInteraction); + }; + + props.document.addEventListener('mouseup', onInteraction); + props.document.addEventListener('keyup', onInteraction); + props.document.addEventListener('touchstart', onInteraction); + + return () => { + props.document.removeEventListener('mouseup', onInteraction); + props.document.removeEventListener('keyup', onInteraction); + props.document.removeEventListener('touchstart', onInteraction); + }; +}; +export const PageInteraction = props => [PageInteractionFx, props]; diff --git a/public/tabs/goals.js b/public/tabs/goals.js index 91dc1a8..21d141b 100644 --- a/public/tabs/goals.js +++ b/public/tabs/goals.js @@ -1,20 +1,78 @@ -import { addGoal } from '/sections/addGoal.js'; +import { h, text } from '/vendor/hyperapp.js'; import { goalList } from '/sections/goalList.js'; -import { h } from '/vendor/hyperapp.js'; -import { removeCompletedGoals } from '/sections/removeCompletedGoals.js'; +import { details } from '/components/details.js'; +import * as actions from '/actions.js'; + +export const goals = props => { + const isEdittingGoal = props.forms.goal.id && props.goals.some(g => g.id === props.forms.goal.id); + const anyGoalsComplete = props.goals.some(g => g.completed); + + return h('div', {}, [ + h('header', { class: 'flex justify-start items-center border-b border-gray-400 mb-2' }, [ + h('h1', { class: 'text-lg font-bold flex-grow' }, text('Goals')), + anyGoalsComplete && h('button', { + class: 'ml-2 dark:text-white text-black underline', + onclick: actions.RemoveCompletedGoals, + }, text('Clear Completed')), + ]), -export const goals = props => - h('div', {}, [ goalList({ expandedReorderable: props.expandedReorderable, drag: props.drag.type === 'goal' ? props.drag : {}, goals: props.goals, lang: props.lang, + forms: props.forms, }), - addGoal({ - goal: props.goal, - addMultiple: props.addMultiple, - lang: props.lang, - }), - removeCompletedGoals({ goals: props.goals, lang: props.lang }), - ]); \ No newline at end of file + + h('form', { + class: 'ml-3 mt-2', + method: 'get', + onsubmit: (_, e) => { + e.preventDefault(); + const formData = new FormData(e.target); + switch (e.submitter.value) { + case 'update': + return [actions.RenameGoal, { id: formData.get('id'), value: formData.get('text') }]; + case 'remove': + return [actions.RemoveGoal, formData.get('id')]; + case 'add': + return [actions.AddGoals, formData.get('text')]; + } + }, + }, [ + h('input', { type: 'hidden', name: 'id', value: props.forms.goal.id }), + details({ which: 'goalForm', details: props.details, summary: 'Show goal form' }, [ + + h('div', { class: 'flex items-end justify-items-start' }, [ + h('fieldset', { class: 'flex-grow' }, [ + h('label', { class: 'mt-3 uppercase leading-none mb-1 text-xs block' }, text('A good day would be...')), + h('input', { + type: 'text', + class: 'bg-transparent border-b border-b-white w-full', + placeholder: 'Finish feature X', + name: 'text', + value: props.forms.goal.input, + required: true, + oninput: (_, e) => [actions.SetFormInput, { form: 'goal', input: e.target.value }], + onkeydown: (_, e) => { + if (e.key === 'Enter' && !isEdittingGoal) { + e.preventDefault(); + return [actions.AddGoals, e.target.value]; + } + if (e.key === 'Enter' && isEdittingGoal) { + e.preventDefault(); + return [actions.RenameGoal, { id: props.forms.goal.id, value: e.target.value }]; + } + return [s => s]; + }, + }), + ]), + !isEdittingGoal && h('button', { type: 'submit', name: 'action', value: 'add', class: 'ml-1 px-2 py-1 border border-slate-700' }, text('Add')), + isEdittingGoal && h('button', { type: 'submit', name: 'action', value: 'update', class: 'ml-1 px-2 py-1 border border-slate-700' }, text('Update')), + isEdittingGoal && h('button', { type: 'submit', name: 'action', value: 'remove', class: 'ml-1 px-2 py-1 border border-slate-700' }, text('Remove')), + isEdittingGoal && h('button', { type: 'button', name: 'action', value: 'cancel', class: 'ml-1 px-2 py-1 border border-slate-700', onclick: () => [actions.SetFormId, { form: 'goal', id: '' }] }, text('Cancel')), + ]), + ]), + ]), + ]); +}; \ No newline at end of file diff --git a/public/tabs/localSettings.js b/public/tabs/localSettings.js new file mode 100644 index 0000000..5a64384 --- /dev/null +++ b/public/tabs/localSettings.js @@ -0,0 +1,72 @@ +import { h, text } from '/vendor/hyperapp.js'; + +import { checkbox } from '/components/checkbox.js'; + +import * as actions from '/actions.js'; + +const audioFiles = [ + { value: 'horn', label: 'Pneumatic horn' }, + { value: 'ding', label: 'Ding' }, + { value: 'applause', label: 'Applause and Cheering' }, + { value: 'bike-bell', label: 'Bike Bell' }, + { value: 'gong', label: 'Gong' }, + { value: 'excuse-me', label: 'Oops, excuse me' }, +]; + +export const localSettings = (props) => { + return h('div', {}, [ + h('header', { class: 'flex justify-start items-center border-b border-gray-400 mb-2' }, [ + h('h1', { class: 'text-lg font-bold flex-grow' }, text('Local settings')), + ]), + h( + 'div', + { + class: 'grid gap-2', + style: { + 'grid-template-columns': '1fr 26px', + }, + }, + [ + h('div', {}, text('Dark mode')), + checkbox({ + checked: props.dark, + oninput: (_, e) => [actions.SetDark, { dark: e.target.checked }], + }), + + h('div', { class: 'flex items-bottom justify-between' }, [ + text('Notifications'), + h('button', { type: 'button', class: 'px-2', onclick: () => [actions.TestNotification, {}] }, text('Test')), + ]), + checkbox({ + checked: props.allowNotification, + oninput: (_, e) => ( + e.target.checked + ? [actions.RequestNotificationPermission, {}] + : [actions.SetAllowNotification, { allowNotification: e.target.checked }] + ), + }), + + h('div', { class: 'flex items-bottom justify-between' }, [ + text('Timer Sound'), + h('div', { class: 'flex-grow flex items-center justify-end' }, [ + h('select', { + class: 'px-2 text-black', + onchange: (_, event) => { + return [actions.SetSound, event.target.value]; + }, + }, audioFiles.map(a => h( + 'option', + { value: a.value, selected: props.sound === a.value }, + text(a.label), + ))), + ]), + h('button', { type: 'button', class: 'px-2', onclick: [actions.TestSound, undefined] }, text('Test')), + ]), + checkbox({ + checked: props.allowSound, + oninput: (_, e) => [actions.SetAllowSound, e.target.checked], + }), + ], + ), + ]); +}; diff --git a/public/tabs/mob.js b/public/tabs/mob.js index 36fd3a2..99b2a43 100644 --- a/public/tabs/mob.js +++ b/public/tabs/mob.js @@ -1,24 +1,71 @@ -import { h } from '/vendor/hyperapp.js'; -import { addParticipant } from '/sections/addParticipant.js'; -import { mobActions } from '/sections/mobActions.js'; +import { h, text } from '/vendor/hyperapp.js'; import { mobParticipants } from '/sections/mobParticipants.js'; +import { details } from '/components/details.js'; +import * as actions from '/actions.js'; -export const mob = props => - h('div', {}, [ +export const mob = props => { + const isEdittingMember = props.forms.mob.id && props.mob.some(m => m.id == props.forms.mob.id); + + return h('div', {}, [ + h('header', { class: 'flex justify-start items-center border-b border-gray-400 mb-2' }, [ + h('h1', { class: 'text-lg font-bold flex-grow' }, text('Team')), + h('button', { + type: 'button', + class: 'ml-2 dark:text-white text-black underline', + onclick: actions.ShuffleMob, + }, text('Shuffle')), + h('button', { + type: 'button', + class: 'ml-2 dark:text-white text-black underline', + onclick: actions.CycleMob, + }, text('Rotate')), + ]), mobParticipants({ expandedReorderable: props.expandedReorderable, drag: props.drag.type === 'mob' ? props.drag : {}, mob: props.mob, mobOrder: props.settings.mobOrder, lang: props.lang, + forms: props.forms, }), - addParticipant({ - name: props.name, - lang: props.lang, - }), + h('form', { + class: 'ml-3 mt-2', + onsubmit: (_, e) => { + e.preventDefault(); + const formData = new FormData(e.target); + switch (e.submitter.value) { + case 'update': + return [actions.RenameUser, { id: formData.get('id'), value: formData.get('name') }]; + case 'remove': + return [actions.RemoveFromMob, formData.get('id')]; + case 'add': + return [actions.AddNameToMob, { name: formData.get('name') }]; + } + }, + }, [ + h('input', { type: 'hidden', name: 'id', value: props.forms.mob.id }), + details({ which: 'mobForm', details: props.details, summary: 'Show member form' }, [ - mobActions({ - lang: props.lang, - }), + h('div', { class: 'flex items-end justify-items-start' }, [ + h('fieldset', { class: 'flex-grow' }, [ + h('label', { class: 'mt-3 uppercase leading-none mb-1 text-xs block' }, text('team member')), + h('input', { + type: 'text', + class: 'bg-transparent border-b border-b-white w-full', + placeholder: 'Name', + name: 'name', + value: props.forms.mob.input, + required: true, + oninput: (_, e) => [actions.SetFormInput, { form: 'mob', input: e.target.value }], + }), + ]), + !isEdittingMember && h('button', { type: 'submit', name: 'action', value: 'add', class: 'ml-1 px-2 py-1 border border-slate-700' }, text('Add')), + isEdittingMember && h('button', { type: 'submit', name: 'action', value: 'update', class: 'ml-1 px-2 py-1 border border-slate-700' }, text('Update')), + isEdittingMember && h('button', { type: 'submit', name: 'action', value: 'remove', class: 'ml-1 px-2 py-1 border border-slate-700' }, text('Remove')), + isEdittingMember && h('button', { type: 'button', name: 'action', value: 'cancel', class: 'ml-1 px-2 py-1 border border-slate-700', onclick: () => [actions.SetFormId, { form: 'mob', id: '' }] }, text('Cancel')), + ]), + ]), + ]), ]); +}; diff --git a/public/tabs/settings.js b/public/tabs/settings.js.old similarity index 85% rename from public/tabs/settings.js rename to public/tabs/settings.js.old index 84fac1f..3feeca8 100644 --- a/public/tabs/settings.js +++ b/public/tabs/settings.js.old @@ -39,36 +39,36 @@ export const settings = props => rightAction: Object.keys(props.pendingSettings).length === 0 ? h('div', {}, [ - text(props.lang.settings.saved), - h('i', { class: 'fas fa-check text-green-500' }), - ]) + text(props.lang.settings.saved), + h('i', { class: 'fas fa-check text-green-500' }), + ]) : h('div', {}, [ - button( - { - class: { - 'bg-indigo-500': true, - 'hover:bg-indigo-400': true, - 'text-white': true, - 'mr-1': true, - }, - onclick: actions.PendingSettingsReset, + button( + { + class: { + 'bg-indigo-500': true, + 'hover:bg-indigo-400': true, + 'text-white': true, + 'mr-1': true, }, - text(props.lang.settings.cancel), - ), - button( - { - type: 'button', - class: { - 'bg-green-600': true, - 'hover:bg-green-500': true, - 'text-white': true, - }, - disable: Object.keys(props.pendingSettings).length === 0, - onclick: actions.UpdateSettings, + onclick: actions.PendingSettingsReset, + }, + text(props.lang.settings.cancel), + ), + button( + { + type: 'button', + class: { + 'bg-green-600': true, + 'hover:bg-green-500': true, + 'text-white': true, }, - text(props.lang.settings.save), - ), - ]), + disable: Object.keys(props.pendingSettings).length === 0, + onclick: actions.UpdateSettings, + }, + text(props.lang.settings.save), + ), + ]), }, text(props.lang.settings.sharedTimerSettings), ), @@ -183,12 +183,12 @@ export const settings = props => }, props.allowSound ? audioFiles.map(({ value, label }) => - h( - 'option', - { value, selected: props.sound === value }, - text(label), - ), - ) + h( + 'option', + { value, selected: props.sound === value }, + text(label), + ), + ) : [ h( 'option', diff --git a/public/tabs/timerSettings.js b/public/tabs/timerSettings.js new file mode 100644 index 0000000..5eb280c --- /dev/null +++ b/public/tabs/timerSettings.js @@ -0,0 +1,91 @@ +import { h, text } from '/vendor/hyperapp.js'; + +import { formattedTimeToMilliseconds } from '/lib/formatTime.js'; +import { formData } from '/lib/form.js'; + +import * as actions from '/actions.js'; + +export const timerSettings = (props) => { + const isDirty = ( + props.settings.duration !== formattedTimeToMilliseconds(props.forms.timerDuration.input) || + props.settings.mobOrder !== props.forms.mobOrder.input + ); + + return h('form', { + onsubmit: (_, event) => { + event.preventDefault(); + if (!event.target.reportValidity()) { + return [s => s]; + } + + const settings = formData(event.target); + settings.duration = formattedTimeToMilliseconds(settings.duration); + + return [actions.SubmitSettings, settings]; + }, + }, [ + h('header', { class: 'flex justify-start items-center border-b border-gray-400 mb-2' }, [ + h('h1', { class: 'text-lg font-bold flex-grow' }, text('Timer settings')), + isDirty && h('button', { + type: 'button', + onclick: [actions.RevertSettings, {}], + class: 'ml-1', + }, text('Revert Changes')), + isDirty && h('button', { + type: 'submit', + class: 'ml-1', + }, text('Save')), + ]), + h( + 'div', + { + class: 'grid gap-2', + style: { + 'grid-template-columns': '3fr 1fr', + }, + }, + [ + h('div', {}, text('Duration')), + h('div', { class: 'flex' }, [ + h('input', { + type: 'text', + pattern: '[0-9]{1,2}(:[0-5][0-9])?', + class: [ + 'mr-2 border-b border-slate-600 dark:bg-transparent dark:text-gray-200', + !props.forms.timerDuration.valid && 'outline outline-red-400', + ].filter(Boolean).join(' '), + oninput: (_, event) => { + const seconds = formattedTimeToMilliseconds(event.target.value) / 1000; + const valid = event.target.checkValidity() && seconds > 0; + + return [actions.SetFormInput, { form: 'timerDuration', input: event.target.value, valid }]; + }, + placeholder: 'mm or mm:ss', + value: props.forms.timerDuration.input, + name: 'duration', + }), + text('minutes'), + ]), + + h('div', {}, text('Positions')), + h('div', { class: 'flex' }, [ + h('input', { + type: 'text', + pattern: '([^,]+,?)+', + class: [ + 'mr-2 border-b border-slate-600 dark:bg-transparent dark:text-gray-200 block w-full', + !props.forms.mobOrder.valid && 'outline outline-red-400', + ].filter(Boolean).join(' '), + oninput: (_, event) => { + return [actions.SetFormInput, { form: 'mobOrder', input: event.target.value, valid: true }]; + }, + placeholder: 'navigator,driver', + value: props.forms.mobOrder.input, + name: 'mobOrder', + title: 'Comma separated list of position names', + }), + ]), + ], + ), + ]); +}; diff --git a/public/timer.html b/public/timer.html index a5d1f0d..5b6db04 100644 --- a/public/timer.html +++ b/public/timer.html @@ -4,6 +4,7 @@ Mob Timer + - - - - +
+ diff --git a/public/timer.js b/public/timer.js index 8667625..afedb4a 100644 --- a/public/timer.js +++ b/public/timer.js @@ -1,14 +1,19 @@ import * as actions from '/actions.js'; -import { card } from '/components/card.js'; -import { appPrompt } from '/components/prompt.js'; +import { grid } from '/components/grid.js'; +import { column } from '/components/column.js'; +import { details } from '/components/details.js'; +import { mob } from '/tabs/mob.js'; +import { goals } from '/tabs/goals.js'; import { header } from '/sections/header.js'; import { timeRemaining } from '/sections/timeRemaining.js'; -import { toasts } from '/sections/toasts.js'; +import { summary } from '/sections/summary.js'; +import { localSettings } from '/tabs/localSettings.js'; +import { timerSettings } from '/tabs/timerSettings.js'; import * as subscriptions from '/subscriptions.js'; -import { app, h } from '/vendor/hyperapp.js'; +import { app, h, text, memo } from '/vendor/hyperapp.js'; import * as Emitter from '/lib/emitter.js'; -import { tabs, showTab } from '/tabs/index.js'; +const stateWithoutFrequentChanges = ({ timerStartedAt, timerDuration, currentTime, mob, goals, ...state }) => state; const cleanState = state => ({ ...state, @@ -29,10 +34,14 @@ const flags = window.location.search }; }, {}); +const darkDefault = ('dark' in flags) || + window.matchMedia('(prefers-color-scheme: dark)').matches; + app({ init: actions.Init(null, { timerId: initialTimerId, externals: { + fetch: window.fetch, documentElement: window.document, Notification: window.Notification, storage: window.localStorage, @@ -41,80 +50,80 @@ app({ socketEmitter: Emitter.make(), }, lang: flags.lang || 'en_CA', - dark: 'dark' in flags, + dark: darkDefault, }), - view: state => - h( - 'div', - { - class: { - 'flex': true, - 'items-start': true, - 'justify-center': true, - 'min-h-screen': true, - }, - }, - [ - card( + view: state => { + const loadingPercent = (state.loading.total - state.loading.messages.length); + const isLoading = loadingPercent < state.loading.total; + + return h('div', {}, [ + state.allowSound && !state.hasInteractedWithPage && h('div', { class: 'text-center px-2 bg-amber-400 text-white' }, [ + h('div', {}, text('You have enabled sound, but never interacted with the page. Click or touch on this page to remove this warning.')), + h('a', { href: 'https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide', target: 'mdn-auto-play', class: 'border-b border-blue-500' }, [ + text('See this MDN article on auto-play for more information'), + h('span', { class: 'ml-1', innerHTML: '⧉' }), + ]), + ]), + grid({ class: 'relative pb-12 px-2' }, [ + column.fixed(2, header(state)), + + column(2, { md: 1 }, [ + h('div', { class: 'flex flex-row items-center justify-center' }, [ + timeRemaining(state), + ]), + ]), + column(2, { md: 1 }, [ + summary(state), + ]), + isLoading && column.fixed(2, [ + h('progress', { max: 4, value: loadingPercent, class: 'w-full', style: { height: '2px' } }), + ]), + !isLoading && column.fixed(2, [ + details({ which: 'summary', details: state.details, summary: 'Your Session' }, [ + grid({}, [ + column(2, { md: 1 }, mob(state)), + column(2, { md: 1 }, goals(state)), + ]), + ]), + ]), + !isLoading && column.fixed(2, [ + details({ which: 'advancedSettings', details: state.details, summary: 'Advanced Settings' }, [ + grid({}, [ + column(2, { md: 1 }, memo(localSettings, stateWithoutFrequentChanges(state))), + column(2, { md: 1 }, memo(timerSettings, stateWithoutFrequentChanges(state))), + ]), + ]), + ]), + + h( + 'audio', { - class: { - 'box-border': true, - 'min-h-screen': true, - 'sm:min-h-0': true, - 'w-full': true, - 'sm:w-8/12': true, - 'md:w-10/12': true, - 'lg:w-6/12': true, - 'shadow': false, - 'sm:shadow-lg': true, - 'pt-2': false, - 'pt-0': true, - 'pb-12': true, - 'pb-1': false, - 'sm:mt-2': true, - 'rounded': false, - 'sm:rounded': true, - 'relative': true, - }, + preload: 'auto', + id: 'timer-complete', + key: state.sound, }, [ - header(state), - timeRemaining(state), - tabs(cleanState(state)), - showTab(cleanState(state)), - h( - 'audio', - { - preload: 'auto', - id: 'timer-complete', - key: state.sound, - }, - [ - h('source', { - src: `/audio/${state.sound}.wav`, - type: 'audio/wav', - }), - ], - ), - - state.prompt.visible && appPrompt(state.prompt || {}), + h('source', { + src: `/audio/${state.sound}.wav`, + type: 'audio/wav', + }), ], ), - toasts(state), - ], - ), + ]), + ]); + }, subscriptions: state => { const { timerId, drag, websocketConnect, externals } = state; return [ websocketConnect && - subscriptions.Websocket({ - actions, - externals, - timerId, - }), + subscriptions.Websocket({ + actions, + externals, + timerId, + }), subscriptions.Timer({ timerStartedAt: state.timerStartedAt, @@ -123,12 +132,18 @@ app({ }), drag.type && - subscriptions.DragAndDrop({ - active: drag.active, - DragMove: actions.DragMove, - DragEnd: actions.DragEnd, - DragCancel: actions.DragCancel, - }), + subscriptions.DragAndDrop({ + active: drag.active, + DragMove: actions.DragMove, + DragEnd: actions.DragEnd, + DragCancel: actions.DragCancel, + }), + + !state.hasInteractedWithPage && + subscriptions.PageInteraction({ + onInteraction: actions.SetPageInteraction, + document: state.externals.documentElement, + }), ]; }, diff --git a/public/vendor/hyperapp.js b/public/vendor/hyperapp.js index c9d2d9e..41c9ab5 100644 --- a/public/vendor/hyperapp.js +++ b/public/vendor/hyperapp.js @@ -2,9 +2,10 @@ import { app, h as originalH, text, -} from 'https://unpkg.com/hyperapp@2.0.20?module=1'; + memo, +} from 'https://unpkg.com/hyperapp@2.0.22?module=1'; -export { app, text }; +export { app, text, memo }; export const h = (tag, props, ...children) => { if (typeof tag === 'function') { console.warn('using h to run custom component', tag); diff --git a/src/web/actions.js b/src/web/actions.js index 2cd3469..3e2475b 100644 --- a/src/web/actions.js +++ b/src/web/actions.js @@ -3,8 +3,9 @@ import { WebSocket } from 'ws'; import { RelayMessage, ShareMessage } from './websocket.js'; import * as Connection from './connection.js'; import { id, GenerateIdEffect } from './id.js'; +import { composable, select, selectArray, replace } from 'composable-state'; -const { none, act, batch, defer } = effects; +const { none, act, batch, defer, thunk } = effects; const defaultStatistics = { connections: 0, @@ -33,6 +34,7 @@ const extractStatistics = message => { export const Init = (queue, nextId = id()) => [ { + completeTokens: {}, connections: {}, queue, nextId, @@ -40,6 +42,20 @@ export const Init = (queue, nextId = id()) => [ none(), ]; +export const SetCompleteToken = (timerId, token) => state => [ + composable(state, selectArray(['completeTokens', timerId], replace(token))), + none(), +]; + +export const RemoveCompleteToken = timerId => state => [ + composable(state, select('completeTokens', replace((old) => { + const clone = { ...old }; + delete clone[timerId]; + return clone; + }))), + none(), +]; + export const SetNextId = nextId => state => [{ ...state, nextId }, none()]; export const UpdateStatisticsFromMessage = (timerId, message) => state => [ @@ -121,10 +137,7 @@ export const UpdateTimer = (timerId, message) => state => { const meta = { ...(type === 'timer:start' ? { timerStartedAt: Date.now() } : {}), - ...(type === 'timer:complete' - ? { timerStartedAt: null, timerDuration: 0 } - : {}), - ...(type === 'timer:pause' ? { timerStartedAt: null } : {}), + ...(type === 'timer:complete' ? { timerStartedAt: null, timerDuration: 0 } : {}), }; return [ @@ -145,6 +158,40 @@ export const UpdateTimer = (timerId, message) => state => { ]; }; +export const CompleteTimer = (timerId, token, statusCallback) => state => { + if (state.completeTokens[timerId] !== token) { + statusCallback(false); + return [state, none()]; + } + + return [ + state, + batch([ + act(RemoveCompleteToken(timerId)), + act(UpdateTimer(timerId, JSON.stringify({ type: 'timer:complete' }))), + thunk(() => { + statusCallback(true); + return none(); + }), + ]), + ]; +}; + +export const PauseTimer = (timerId, token, duration) => state => { + if (state.completeTokens[timerId] !== token) { + return [state, none()]; + } + + return [ + state, + batch([ + //defer(state.queue.publishToTimer(timerId, JSON.stringify({ type: 'timer:pause', timerDuration: duration })).then(none)), + act(RemoveCompleteToken(timerId)), + act(UpdateTimer(timerId, JSON.stringify({ type: 'timer:pause', timerStartedAt: null, timerDuration: duration }))), + ]), + ]; +}; + export const MessageTimer = (timerId, message) => state => { return [ state, @@ -174,24 +221,30 @@ export const ShareTimerWith = (connection, timerId) => state => [ ), ); - const timerIsRunning = - timer.timerStartedAt && - timer.timerDuration && - timer.timerDuration - (Date.now() - timer.timerStartedAt) > 0; - - return batch( - [ - sync('settings'), - sync('mob'), - sync('goals'), - timerIsRunning && - sync('timer', { - type: 'timer:update', - timerStartedAt: timer.timerStartedAt, - timerDuration: timer.timerDuration, - }), - ].filter(Boolean), - ); + const timerData = { + type: 'timer:update', + timerStartedAt: timer.timerStartedAt, + timerDuration: timer.timerDuration, + completeToken: state.completeTokens[timerId] || null, + }; + + const timerEndsAt = timer.timerStartedAt + timer.timerDuration; + if (Date.now() > timerEndsAt) { + timerData.timerStartedAt = null; + timerData.timerDuration = 0; + timerData.completeToken = null; + } + + return batch([ + sync('settings'), + sync('mob'), + sync('goals'), + sync('timer', timerData), + sync('connections', { + type: 'connections:update', + count: state.connections[timerId].length, + }), + ]); }), ), ]; diff --git a/src/web/actions.test.js b/src/web/actions.test.js index 845c024..2de9244 100644 --- a/src/web/actions.test.js +++ b/src/web/actions.test.js @@ -34,6 +34,7 @@ test('Init resets connections and statistics, with no effect', async t => { t.truthy(ok()); t.deepEqual(state(), { + completeTokens: {}, connections: {}, queue, nextId, diff --git a/src/web/http.js b/src/web/http.js index a43d379..338b88e 100644 --- a/src/web/http.js +++ b/src/web/http.js @@ -61,6 +61,52 @@ const HttpSub = (dispatch, action, host = 'localhost', port = 4321) => { return response.status(200).send(html); }); + app.post('/:timerId/timer/start', async (request, response) => { + const { timerId } = request.params; + + const completeToken = Math.random().toString(36).slice(-6); + await dispatch(action.SetCompleteToken(timerId, completeToken), 'SetCompleteToken'); + + const message = JSON.stringify({ + type: 'timer:start', + timerDuration: request.body.duration, + completeToken, + }); + + await dispatch(action.UpdateTimer(timerId, message), 'UpdateTimer'); + + return response.status(202).json({ completeToken }); + }); + + app.post('/:timerId/timer/pause', async (request, response) => { + const { timerId } = request.params; + const { token, duration } = request.body; + + console.log('http timer pause', { timerId, token, duration }, request.body); + + await dispatch(action.PauseTimer(timerId, token, duration), 'PauseTimer'); + return response.status(202).json({}); + }); + + app.post('/:timerId/timer/complete', async (request, response) => { + const { timerId } = request.params; + const { token } = request.body; + + const responder = (success) => { + return response.status(success ? 202 : 410).json({}); + }; + + await dispatch(action.CompleteTimer(timerId, token, responder), 'CompleteTimer'); + }); + + app.post('/:timerId/timer/settings', async (request, response) => { + const { timerId } = request.params; + const { settings } = request.body; + + await dispatch(action.UpdateTimer(timerId, JSON.stringify({ type: 'settings:update', settings })), 'UpdateTimer'); + return response.status(202).json({}); + }); + server.listen(port, host, () => { console.log(`Local server up: http://${host}:${port}`); }); diff --git a/src/web/websocket.js b/src/web/websocket.js index a8cfb89..48206a1 100644 --- a/src/web/websocket.js +++ b/src/web/websocket.js @@ -26,8 +26,18 @@ const WebsocketSub = (dispatch, actions, connection, timerId) => { log('error'); }); + const ignoredMessageTypes = [ + 'timer:start', + 'timer:pause', + 'timer:complete', + ]; websocket.on('message', payload => { log('message', payload.toString()); + const { type } = JSON.parse(payload.toString()); + if (ignoredMessageTypes.includes(type)) { + log('WARN', 'socket sent ignored instruction', type); + return; + } return dispatch( actions.UpdateTimer(timerId, payload.toString()), 'UpdateTimer', diff --git a/tailwind.config.cjs b/tailwind.config.cjs index d636dd9..e21d5c9 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -1,4 +1,3 @@ -console.log('tailwind loaded config'); module.exports = { darkMode: 'class', content: [