diff --git a/client/package-lock.json b/client/package-lock.json
index 5fbb9c8..b8c35fe 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -65,13 +65,20 @@
}
},
"@babel/generator": {
- "version": "7.11.5",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.5.tgz",
- "integrity": "sha512-9UqHWJ4IwRTy4l0o8gq2ef8ws8UPzvtMkVKjTLAiRmza9p9V6Z+OfuNd9fB1j5Q67F+dVJtPC2sZXI8NM9br4g==",
+ "version": "7.11.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
+ "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
"requires": {
"@babel/types": "^7.11.5",
"jsesc": "^2.5.1",
- "source-map": "^0.6.1"
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
}
},
"@babel/helper-annotate-as-pure": {
@@ -1761,9 +1768,9 @@
"integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA=="
},
"@types/babel__core": {
- "version": "7.1.9",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz",
- "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==",
+ "version": "7.1.10",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.10.tgz",
+ "integrity": "sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw==",
"requires": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0",
@@ -1773,26 +1780,26 @@
}
},
"@types/babel__generator": {
- "version": "7.6.1",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz",
- "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==",
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.2.tgz",
+ "integrity": "sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==",
"requires": {
"@babel/types": "^7.0.0"
}
},
"@types/babel__template": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz",
- "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==",
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.3.tgz",
+ "integrity": "sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==",
"requires": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0"
}
},
"@types/babel__traverse": {
- "version": "7.0.13",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.13.tgz",
- "integrity": "sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ==",
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz",
+ "integrity": "sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==",
"requires": {
"@babel/types": "^7.3.0"
}
@@ -2059,6 +2066,11 @@
}
}
},
+ "@types/uuid": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz",
+ "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ=="
+ },
"@types/yargs": {
"version": "13.0.10",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.10.tgz",
@@ -2294,6 +2306,11 @@
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
+ "abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
+ },
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -2303,10 +2320,31 @@
"negotiator": "0.6.2"
}
},
+ "acorn": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
+ "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
+ },
+ "acorn-globals": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
+ "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
+ "requires": {
+ "acorn": "^6.0.1",
+ "acorn-walk": "^6.0.1"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+ "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
+ }
+ }
+ },
"acorn-jsx": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
- "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ=="
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
+ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng=="
},
"acorn-walk": {
"version": "6.2.0",
@@ -2370,9 +2408,9 @@
}
},
"ajv": {
- "version": "6.12.4",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
- "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
+ "version": "6.12.5",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
+ "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -2509,6 +2547,26 @@
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0",
"is-string": "^1.0.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"array-union": {
@@ -2536,6 +2594,26 @@
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"arrify": {
@@ -3355,6 +3433,13 @@
"parse-asn1": "^5.1.5",
"readable-stream": "^3.6.0",
"safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ }
}
},
"browserify-zlib": {
@@ -3366,14 +3451,14 @@
}
},
"browserslist": {
- "version": "4.14.0",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz",
- "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==",
+ "version": "4.14.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz",
+ "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==",
"requires": {
- "caniuse-lite": "^1.0.30001111",
- "electron-to-chromium": "^1.3.523",
- "escalade": "^3.0.2",
- "node-releases": "^1.1.60"
+ "caniuse-lite": "^1.0.30001135",
+ "electron-to-chromium": "^1.3.571",
+ "escalade": "^3.1.0",
+ "node-releases": "^1.1.61"
}
},
"bser": {
@@ -3534,9 +3619,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001122",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001122.tgz",
- "integrity": "sha512-pxjw28CThdrqfz06nJkpAc5SXM404TXB/h5f4UJX+rrXJKE/1bu/KAILc2AY+O6cQIFtRjV9qOR2vaEp9LDGUA=="
+ "version": "1.0.30001140",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001140.tgz",
+ "integrity": "sha512-xFtvBtfGrpjTOxTpjP5F2LmN04/ZGfYV8EQzUIC/RmKpdrmzJrjqlJ4ho7sGuAMPko2/Jl08h7x9uObCfBFaAA=="
},
"canvg": {
"version": "3.0.6",
@@ -3788,11 +3873,6 @@
"q": "^1.1.2"
}
},
- "code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
- },
"collection-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -3908,11 +3988,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
@@ -3951,11 +4026,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -3997,13 +4067,6 @@
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
- },
- "dependencies": {
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- }
}
},
"content-type": {
@@ -4017,13 +4080,6 @@
"integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
"requires": {
"safe-buffer": "~5.1.1"
- },
- "dependencies": {
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- }
}
},
"cookie": {
@@ -4438,6 +4494,14 @@
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
},
+ "cssstyle": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
+ "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
+ "requires": {
+ "cssom": "0.3.x"
+ }
+ },
"csstype": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz",
@@ -4480,24 +4544,6 @@
"whatwg-url": "^7.0.0"
},
"dependencies": {
- "abab": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz",
- "integrity": "sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ=="
- },
- "tr46": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
- "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
- "requires": {
- "punycode": "^2.1.0"
- }
- },
- "webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
- },
"whatwg-url": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
@@ -4521,11 +4567,11 @@
"integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
},
"debug": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
- "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"requires": {
- "ms": "^2.1.1"
+ "ms": "2.1.2"
}
},
"decamelize": {
@@ -4828,9 +4874,9 @@
},
"dependencies": {
"domelementtype": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
- "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
+ "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA=="
}
}
},
@@ -4850,13 +4896,6 @@
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
"requires": {
"webidl-conversions": "^4.0.2"
- },
- "dependencies": {
- "webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
- }
}
},
"domhandler": {
@@ -4892,9 +4931,9 @@
}
},
"dot-prop": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
- "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
"requires": {
"is-obj": "^2.0.0"
}
@@ -4944,11 +4983,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -4974,9 +5008,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"electron-to-chromium": {
- "version": "1.3.556",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.556.tgz",
- "integrity": "sha512-g5cGpg6rOCXxyfaLCQIWz9Fx+raFfbZ6sc4QLfvvaiCERBzY6YD6rh5d12QN++bEF1Tm9osYnxP37lbN/92j4A=="
+ "version": "1.3.576",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.576.tgz",
+ "integrity": "sha512-uSEI0XZ//5ic+0NdOqlxp0liCD44ck20OAGyLMSymIWTEAtHKVJi6JM18acOnRgUgX7Q65QqnI+sNncNvIy8ew=="
},
"elliptic": {
"version": "6.5.3",
@@ -5060,11 +5094,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -5097,17 +5126,18 @@
}
},
"es-abstract": {
- "version": "1.17.6",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
- "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "version": "1.18.0-next.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
+ "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.0",
- "is-regex": "^1.1.0",
- "object-inspect": "^1.7.0",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.0",
"string.prototype.trimend": "^1.0.1",
@@ -5154,9 +5184,9 @@
}
},
"escalade": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz",
- "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ=="
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
+ "integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig=="
},
"escape-html": {
"version": "1.0.3",
@@ -5595,11 +5625,11 @@
"integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA=="
},
"eslint-scope": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz",
- "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"requires": {
- "esrecurse": "^4.1.0",
+ "esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
}
},
@@ -5624,13 +5654,6 @@
"acorn": "^7.1.1",
"acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.1.0"
- },
- "dependencies": {
- "acorn": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
- "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w=="
- }
}
},
"esprima": {
@@ -5856,11 +5879,6 @@
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
@@ -6203,11 +6221,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -6319,11 +6332,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -6382,11 +6390,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -6656,6 +6659,13 @@
"inherits": "^2.0.4",
"readable-stream": "^3.6.0",
"safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ }
}
},
"hash.js": {
@@ -6743,11 +6753,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -6889,11 +6894,6 @@
}
}
},
- "http-parser-js": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz",
- "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ=="
- },
"http-proxy": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
@@ -7148,6 +7148,26 @@
"es-abstract": "^1.17.0-next.1",
"has": "^1.0.3",
"side-channel": "^1.0.2"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"invariant": {
@@ -7158,11 +7178,6 @@
"loose-envify": "^1.0.0"
}
},
- "invert-kv": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
- "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA=="
- },
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
@@ -7215,9 +7230,9 @@
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"is-callable": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
- "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw=="
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
},
"is-ci": {
"version": "2.0.0",
@@ -7615,108 +7630,6 @@
"jest-mock": "^24.9.0",
"jest-util": "^24.9.0",
"jsdom": "^11.5.1"
- },
- "dependencies": {
- "abab": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz",
- "integrity": "sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ=="
- },
- "acorn": {
- "version": "5.7.4",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
- "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="
- },
- "acorn-globals": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
- "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
- "requires": {
- "acorn": "^6.0.1",
- "acorn-walk": "^6.0.1"
- },
- "dependencies": {
- "acorn": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
- "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
- }
- }
- },
- "cssstyle": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
- "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
- "requires": {
- "cssom": "0.3.x"
- }
- },
- "jsdom": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
- "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
- "requires": {
- "abab": "^2.0.0",
- "acorn": "^5.5.3",
- "acorn-globals": "^4.1.0",
- "array-equal": "^1.0.0",
- "cssom": ">= 0.3.2 < 0.4.0",
- "cssstyle": "^1.0.0",
- "data-urls": "^1.0.0",
- "domexception": "^1.0.1",
- "escodegen": "^1.9.1",
- "html-encoding-sniffer": "^1.0.2",
- "left-pad": "^1.3.0",
- "nwsapi": "^2.0.7",
- "parse5": "4.0.0",
- "pn": "^1.1.0",
- "request": "^2.87.0",
- "request-promise-native": "^1.0.5",
- "sax": "^1.2.4",
- "symbol-tree": "^3.2.2",
- "tough-cookie": "^2.3.4",
- "w3c-hr-time": "^1.0.1",
- "webidl-conversions": "^4.0.2",
- "whatwg-encoding": "^1.0.3",
- "whatwg-mimetype": "^2.1.0",
- "whatwg-url": "^6.4.1",
- "ws": "^5.2.0",
- "xml-name-validator": "^3.0.0"
- }
- },
- "parse5": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
- "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
- },
- "tr46": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
- "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
- "requires": {
- "punycode": "^2.1.0"
- }
- },
- "webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
- },
- "whatwg-url": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
- "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
- "requires": {
- "lodash.sortby": "^4.7.0",
- "tr46": "^1.0.1",
- "webidl-conversions": "^4.0.2"
- }
- },
- "xml-name-validator": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
- "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
- }
}
},
"jest-environment-jsdom-fourteen": {
@@ -7732,33 +7645,11 @@
"jsdom": "^14.1.0"
},
"dependencies": {
- "abab": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz",
- "integrity": "sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ=="
- },
"acorn": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
},
- "acorn-globals": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
- "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
- "requires": {
- "acorn": "^6.0.1",
- "acorn-walk": "^6.0.1"
- }
- },
- "cssstyle": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
- "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
- "requires": {
- "cssom": "0.3.x"
- }
- },
"jsdom": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz",
@@ -7797,19 +7688,6 @@
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
"integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
},
- "tr46": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
- "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
- "requires": {
- "punycode": "^2.1.0"
- }
- },
- "webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
- },
"whatwg-url": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
@@ -7827,11 +7705,6 @@
"requires": {
"async-limiter": "~1.0.0"
}
- },
- "xml-name-validator": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
- "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
}
}
},
@@ -8186,6 +8059,46 @@
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
+ "jsdom": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
+ "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
+ "requires": {
+ "abab": "^2.0.0",
+ "acorn": "^5.5.3",
+ "acorn-globals": "^4.1.0",
+ "array-equal": "^1.0.0",
+ "cssom": ">= 0.3.2 < 0.4.0",
+ "cssstyle": "^1.0.0",
+ "data-urls": "^1.0.0",
+ "domexception": "^1.0.1",
+ "escodegen": "^1.9.1",
+ "html-encoding-sniffer": "^1.0.2",
+ "left-pad": "^1.3.0",
+ "nwsapi": "^2.0.7",
+ "parse5": "4.0.0",
+ "pn": "^1.1.0",
+ "request": "^2.87.0",
+ "request-promise-native": "^1.0.5",
+ "sax": "^1.2.4",
+ "symbol-tree": "^3.2.2",
+ "tough-cookie": "^2.3.4",
+ "w3c-hr-time": "^1.0.1",
+ "webidl-conversions": "^4.0.2",
+ "whatwg-encoding": "^1.0.3",
+ "whatwg-mimetype": "^2.1.0",
+ "whatwg-url": "^6.4.1",
+ "ws": "^5.2.0",
+ "xml-name-validator": "^3.0.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "5.7.4",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
+ "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="
+ }
+ }
+ },
"jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
@@ -8197,9 +8110,9 @@
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
"json-parse-even-better-errors": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.0.tgz",
- "integrity": "sha512-o3aP+RsWDJZayj1SbHNQAI8x0v3T3SKiGoZlNYfbUP1S3omJQ6i9CnqADqkSPaOAxwua4/1YWx5CM7oiChJt2Q=="
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"json-schema": {
"version": "0.2.3",
@@ -8415,14 +8328,6 @@
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
"integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
},
- "lcid": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
- "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
- "requires": {
- "invert-kv": "^2.0.0"
- }
- },
"left-pad": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
@@ -8658,14 +8563,6 @@
"resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
"integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA=="
},
- "map-age-cleaner": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
- "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
- "requires": {
- "p-defer": "^1.0.0"
- }
- },
"map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@@ -8723,16 +8620,6 @@
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
- "mem": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
- "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
- "requires": {
- "map-age-cleaner": "^0.1.1",
- "mimic-fn": "^2.0.0",
- "p-is-promise": "^2.0.0"
- }
- },
"memoize-one": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
@@ -8766,11 +8653,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -9131,9 +9013,9 @@
}
},
"node-forge": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
- "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ=="
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
},
"node-int64": {
"version": "0.4.0",
@@ -9204,11 +9086,6 @@
}
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"util": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
@@ -9251,9 +9128,9 @@
}
},
"node-releases": {
- "version": "1.1.60",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz",
- "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA=="
+ "version": "1.1.61",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz",
+ "integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g=="
},
"normalize-package-data": {
"version": "2.5.0",
@@ -9318,11 +9195,6 @@
"resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
"integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4="
},
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
- },
"nwsapi": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
@@ -9375,6 +9247,26 @@
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"object-keys": {
@@ -9396,14 +9288,14 @@
}
},
"object.assign": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
- "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
- "define-properties": "^1.1.2",
- "function-bind": "^1.1.1",
- "has-symbols": "^1.0.0",
- "object-keys": "^1.0.11"
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.0",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
}
},
"object.entries": {
@@ -9414,6 +9306,26 @@
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5",
"has": "^1.0.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"object.fromentries": {
@@ -9425,6 +9337,26 @@
"es-abstract": "^1.17.0-next.1",
"function-bind": "^1.1.1",
"has": "^1.0.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"object.getownpropertydescriptors": {
@@ -9434,6 +9366,26 @@
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"object.pick": {
@@ -9453,6 +9405,26 @@
"es-abstract": "^1.17.0-next.1",
"function-bind": "^1.1.1",
"has": "^1.0.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"obuf": {
@@ -9490,9 +9462,9 @@
}
},
"open": {
- "version": "7.2.1",
- "resolved": "https://registry.npmjs.org/open/-/open-7.2.1.tgz",
- "integrity": "sha512-xbYCJib4spUdmcs0g/2mK1nKo/jO2T7INClWd/beL7PFkXRWgr8B23ssDHX/USPn2M2IjDR5UdpYs6I67SnTSA==",
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz",
+ "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==",
"requires": {
"is-docker": "^2.0.0",
"is-wsl": "^2.1.1"
@@ -9551,26 +9523,11 @@
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
"integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc="
},
- "os-locale": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
- "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
- "requires": {
- "execa": "^1.0.0",
- "lcid": "^2.0.0",
- "mem": "^4.0.0"
- }
- },
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
- "p-defer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
- "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww="
- },
"p-each-series": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz",
@@ -9584,11 +9541,6 @@
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
- "p-is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
- "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg=="
- },
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
@@ -9665,11 +9617,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -9725,6 +9672,11 @@
"json-parse-better-errors": "^1.0.1"
}
},
+ "parse5": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+ "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
+ },
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -9903,9 +9855,9 @@
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
},
"postcss": {
- "version": "7.0.32",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
- "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
+ "version": "7.0.35",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
+ "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
@@ -10219,9 +10171,9 @@
}
},
"postcss-load-config": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz",
- "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz",
+ "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==",
"requires": {
"cosmiconfig": "^5.0.0",
"import-cwd": "^2.0.0"
@@ -10770,13 +10722,14 @@
}
},
"postcss-selector-parser": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
- "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
+ "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==",
"requires": {
"cssesc": "^3.0.0",
"indexes-of": "^1.0.1",
- "uniq": "^1.0.1"
+ "uniq": "^1.0.1",
+ "util-deprecate": "^1.0.2"
}
},
"postcss-svgo": {
@@ -11387,9 +11340,9 @@
}
},
"react-scripts": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.1.tgz",
- "integrity": "sha512-JpTdi/0Sfd31mZA6Ukx+lq5j1JoKItX7qqEK4OiACjVQletM1P38g49d9/D0yTxp9FrSF+xpJFStkGgKEIRjlQ==",
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.3.tgz",
+ "integrity": "sha512-oSnoWmii/iKdeQiwaO6map1lUaZLmG0xIUyb/HwCVFLT7gNbj8JZ9RmpvMCZ4fB98ZUMRfNmp/ft8uy/xD1RLA==",
"requires": {
"@babel/core": "7.9.0",
"@svgr/webpack": "4.3.3",
@@ -11437,11 +11390,11 @@
"sass-loader": "8.0.2",
"semver": "6.3.0",
"style-loader": "0.23.1",
- "terser-webpack-plugin": "2.3.5",
+ "terser-webpack-plugin": "2.3.8",
"ts-pnp": "1.1.6",
"url-loader": "2.3.0",
"webpack": "4.42.0",
- "webpack-dev-server": "3.10.3",
+ "webpack-dev-server": "3.11.0",
"webpack-manifest-plugin": "2.2.0",
"workbox-webpack-plugin": "4.3.1"
}
@@ -11575,6 +11528,26 @@
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"regexpp": {
@@ -11583,9 +11556,9 @@
"integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q=="
},
"regexpu-core": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
- "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
+ "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
"requires": {
"regenerate": "^1.4.0",
"regenerate-unicode-properties": "^8.2.0",
@@ -11712,6 +11685,13 @@
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+ }
}
},
"request-promise-core": {
@@ -11938,17 +11918,17 @@
}
},
"rxjs": {
- "version": "6.6.2",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
- "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"requires": {
"tslib": "^1.9.0"
}
},
"safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safe-regex": {
"version": "1.1.0",
@@ -12059,11 +12039,11 @@
"integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo="
},
"selfsigned": {
- "version": "1.10.7",
- "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
- "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
+ "version": "1.10.8",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz",
+ "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==",
"requires": {
- "node-forge": "0.9.0"
+ "node-forge": "^0.10.0"
}
},
"semver": {
@@ -12119,9 +12099,12 @@
}
},
"serialize-javascript": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
- "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ=="
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
},
"serve-index": {
"version": "1.9.1",
@@ -12285,27 +12268,6 @@
"requires": {
"es-abstract": "^1.18.0-next.0",
"object-inspect": "^1.8.0"
- },
- "dependencies": {
- "es-abstract": {
- "version": "1.18.0-next.0",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
- "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
- "requires": {
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.1",
- "is-callable": "^1.2.0",
- "is-negative-zero": "^2.0.0",
- "is-regex": "^1.1.1",
- "object-inspect": "^1.8.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.0",
- "string.prototype.trimend": "^1.0.1",
- "string.prototype.trimstart": "^1.0.1"
- }
- }
}
},
"signal-exit": {
@@ -12466,12 +12428,20 @@
}
},
"sockjs": {
- "version": "0.3.19",
- "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
- "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
+ "version": "0.3.20",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
+ "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==",
"requires": {
"faye-websocket": "^0.10.0",
- "uuid": "^3.0.1"
+ "uuid": "^3.4.0",
+ "websocket-driver": "0.6.5"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+ }
}
},
"sockjs-client": {
@@ -12573,9 +12543,9 @@
}
},
"spdx-license-ids": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
- "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
+ "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw=="
},
"spdy": {
"version": "4.0.2",
@@ -12713,11 +12683,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -12768,11 +12733,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -12853,6 +12813,26 @@
"internal-slot": "^1.0.2",
"regexp.prototype.flags": "^1.3.0",
"side-channel": "^1.0.2"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"string.prototype.trimend": {
@@ -12862,6 +12842,26 @@
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"string.prototype.trimstart": {
@@ -12871,6 +12871,26 @@
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"string_decoder": {
@@ -12879,6 +12899,13 @@
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ }
}
},
"stringify-object": {
@@ -13073,18 +13100,18 @@
}
},
"terser-webpack-plugin": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.5.tgz",
- "integrity": "sha512-WlWksUoq+E4+JlJ+h+U+QUzXpcsMSSNXkDy9lBVkSqDn1w23Gg29L/ary9GeJVYCGiNJJX7LnVc4bwL1N3/g1w==",
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
+ "integrity": "sha512-/fKw3R+hWyHfYx7Bv6oPqmk4HGQcrWLtV3X6ggvPuwPNHSnzvVV51z6OaaCOus4YLjutYGOz3pEpbhe6Up2s1w==",
"requires": {
"cacache": "^13.0.1",
- "find-cache-dir": "^3.2.0",
- "jest-worker": "^25.1.0",
- "p-limit": "^2.2.2",
- "schema-utils": "^2.6.4",
- "serialize-javascript": "^2.1.2",
+ "find-cache-dir": "^3.3.1",
+ "jest-worker": "^25.4.0",
+ "p-limit": "^2.3.0",
+ "schema-utils": "^2.6.6",
+ "serialize-javascript": "^4.0.0",
"source-map": "^0.6.1",
- "terser": "^4.4.3",
+ "terser": "^4.6.12",
"webpack-sources": "^1.4.3"
},
"dependencies": {
@@ -13222,11 +13249,6 @@
"util-deprecate": "~1.0.1"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -13330,6 +13352,14 @@
"punycode": "^2.1.1"
}
},
+ "tr46": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
"ts-pnp": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz",
@@ -13612,6 +13642,26 @@
"es-abstract": "^1.17.2",
"has-symbols": "^1.0.1",
"object.getownpropertydescriptors": "^2.1.0"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.6",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+ "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.0",
+ "is-regex": "^1.1.0",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
}
},
"utila": {
@@ -13625,9 +13675,9 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
+ "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ=="
},
"v8-compile-cache": {
"version": "2.1.1",
@@ -13689,18 +13739,6 @@
"domexception": "^1.0.1",
"webidl-conversions": "^4.0.2",
"xml-name-validator": "^3.0.0"
- },
- "dependencies": {
- "webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
- },
- "xml-name-validator": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
- "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
- }
}
},
"wait-for-expect": {
@@ -13836,12 +13874,6 @@
"readable-stream": "^2.0.2"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "optional": true
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -13861,6 +13893,11 @@
"minimalistic-assert": "^1.0.0"
}
},
+ "webidl-conversions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
+ },
"webpack": {
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz",
@@ -13937,14 +13974,6 @@
"ajv-keywords": "^3.1.0"
}
},
- "serialize-javascript": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
- "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
- "requires": {
- "randombytes": "^2.1.0"
- }
- },
"ssri": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
@@ -13984,9 +14013,9 @@
}
},
"webpack-dev-server": {
- "version": "3.10.3",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz",
- "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==",
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz",
+ "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==",
"requires": {
"ansi-html": "0.0.7",
"bonjour": "^3.5.0",
@@ -13996,31 +14025,31 @@
"debug": "^4.1.1",
"del": "^4.1.1",
"express": "^4.17.1",
- "html-entities": "^1.2.1",
+ "html-entities": "^1.3.1",
"http-proxy-middleware": "0.19.1",
"import-local": "^2.0.0",
"internal-ip": "^4.3.0",
"ip": "^1.1.5",
"is-absolute-url": "^3.0.3",
"killable": "^1.0.1",
- "loglevel": "^1.6.6",
+ "loglevel": "^1.6.8",
"opn": "^5.5.0",
"p-retry": "^3.0.1",
- "portfinder": "^1.0.25",
+ "portfinder": "^1.0.26",
"schema-utils": "^1.0.0",
"selfsigned": "^1.10.7",
"semver": "^6.3.0",
"serve-index": "^1.9.1",
- "sockjs": "0.3.19",
+ "sockjs": "0.3.20",
"sockjs-client": "1.4.0",
- "spdy": "^4.0.1",
+ "spdy": "^4.0.2",
"strip-ansi": "^3.0.1",
"supports-color": "^6.1.0",
"url": "^0.11.0",
"webpack-dev-middleware": "^3.7.2",
"webpack-log": "^2.0.0",
"ws": "^6.2.1",
- "yargs": "12.0.5"
+ "yargs": "^13.3.2"
},
"dependencies": {
"ansi-regex": {
@@ -14052,42 +14081,12 @@
"upath": "^1.1.1"
}
},
- "cliui": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
- "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
- "requires": {
- "string-width": "^2.1.1",
- "strip-ansi": "^4.0.0",
- "wrap-ansi": "^2.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- }
- }
- },
"fsevents": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"optional": true
},
- "get-caller-file": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
- },
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@@ -14120,11 +14119,6 @@
"binary-extensions": "^1.0.0"
}
},
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
- },
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -14159,16 +14153,6 @@
"readable-stream": "^2.0.2"
}
},
- "require-main-filename": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
- },
"schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -14179,30 +14163,6 @@
"ajv-keywords": "^3.1.0"
}
},
- "string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "requires": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- }
- }
- },
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -14227,35 +14187,6 @@
"has-flag": "^3.0.0"
}
},
- "wrap-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
- "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
- "requires": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1"
- },
- "dependencies": {
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "requires": {
- "number-is-nan": "^1.0.0"
- }
- },
- "string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "requires": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- }
- }
- }
- },
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
@@ -14263,34 +14194,6 @@
"requires": {
"async-limiter": "~1.0.0"
}
- },
- "yargs": {
- "version": "12.0.5",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
- "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
- "requires": {
- "cliui": "^4.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^3.0.0",
- "get-caller-file": "^1.0.1",
- "os-locale": "^3.0.0",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^2.0.0",
- "which-module": "^2.0.0",
- "y18n": "^3.2.1 || ^4.0.0",
- "yargs-parser": "^11.1.1"
- }
- },
- "yargs-parser": {
- "version": "11.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
- "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
- "requires": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
}
}
},
@@ -14301,6 +14204,13 @@
"requires": {
"ansi-colors": "^3.0.0",
"uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+ }
}
},
"webpack-manifest-plugin": {
@@ -14336,12 +14246,10 @@
}
},
"websocket-driver": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
- "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz",
+ "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=",
"requires": {
- "http-parser-js": ">=0.5.1",
- "safe-buffer": ">=5.1.0",
"websocket-extensions": ">=0.1.1"
}
},
@@ -14359,15 +14267,25 @@
}
},
"whatwg-fetch": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz",
- "integrity": "sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ=="
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz",
+ "integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ=="
},
"whatwg-mimetype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
"integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
},
+ "whatwg-url": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
+ "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -14626,6 +14544,11 @@
"async-limiter": "~1.0.0"
}
},
+ "xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
+ },
"xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
diff --git a/client/package.json b/client/package.json
index fec4ea1..1fb3ac0 100644
--- a/client/package.json
+++ b/client/package.json
@@ -15,6 +15,7 @@
"@types/react-dom": "^16.9.0",
"@types/react-router": "^5.1.8",
"@types/react-router-dom": "^5.1.5",
+ "@types/uuid": "^8.3.0",
"jsurl": "^0.1.5",
"lodash": "^4.17.20",
"material-table": "^1.69.0",
@@ -24,8 +25,9 @@
"react-dnd-html5-backend": "^11.1.3",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
- "react-scripts": "3.4.1",
- "typescript": "~3.7.2"
+ "react-scripts": "^3.4.3",
+ "typescript": "~3.7.2",
+ "uuid": "^8.3.0"
},
"scripts": {
"dev": "BROWSER=none react-scripts start",
diff --git a/client/src/api.ts b/client/src/api.ts
index b671e3e..7811d5d 100644
--- a/client/src/api.ts
+++ b/client/src/api.ts
@@ -302,4 +302,20 @@ export interface TagDetailsResponse {
}
export function checkTagDetailsRequest(req: any): boolean {
return true;
+}
+
+// Delete tag (DELETE).
+export const DeleteTagEndpoint = '/tag/:id';
+export interface DeleteTagRequest { }
+export interface DeleteTagResponse { }
+export function checkDeleteTagRequest(req: any): boolean {
+ return true;
+}
+
+// Merge tag (POST).
+export const MergeTagEndpoint = '/tag/:id/merge/:toId';
+export interface MergeTagRequest { }
+export interface MergeTagResponse { }
+export function checkMergeTagRequest(req: any): boolean {
+ return true;
}
\ No newline at end of file
diff --git a/client/src/components/MainWindow.tsx b/client/src/components/MainWindow.tsx
index d3b73f1..d600867 100644
--- a/client/src/components/MainWindow.tsx
+++ b/client/src/components/MainWindow.tsx
@@ -2,13 +2,14 @@ import React, { useReducer, Reducer } from 'react';
import { ThemeProvider, CssBaseline, createMuiTheme } from '@material-ui/core';
import { grey } from '@material-ui/core/colors';
import AppBar from './appbar/AppBar';
-import QueryWindow from './windows/QueryWindow';
+import QueryWindow from './windows/query/QueryWindow';
import { NewTabProps } from './appbar/AddTabMenu';
import { newWindowState, newWindowReducer, WindowType } from './windows/Windows';
-import ArtistWindow from './windows/ArtistWindow';
-import AlbumWindow from './windows/AlbumWindow';
-import TagWindow from './windows/TagWindow';
-import SongWindow from './windows/SongWindow';
+import ArtistWindow from './windows/artist/ArtistWindow';
+import AlbumWindow from './windows/album/AlbumWindow';
+import TagWindow from './windows/tag/TagWindow';
+import SongWindow from './windows/song/SongWindow';
+import ManageTagsWindow from './windows/manage_tags/ManageTagsWindow';
var _ = require('lodash');
const darkTheme = createMuiTheme({
@@ -76,6 +77,7 @@ export default function MainWindow(props: any) {
newWindowState[WindowType.Album](),
newWindowState[WindowType.Artist](),
newWindowState[WindowType.Tag](),
+ newWindowState[WindowType.ManageTags](),
],
tabReducers: [
newWindowReducer[WindowType.Query],
@@ -83,6 +85,7 @@ export default function MainWindow(props: any) {
newWindowReducer[WindowType.Album],
newWindowReducer[WindowType.Artist],
newWindowReducer[WindowType.Tag],
+ newWindowReducer[WindowType.ManageTags],
],
tabTypes: [
WindowType.Query,
@@ -90,6 +93,7 @@ export default function MainWindow(props: any) {
WindowType.Album,
WindowType.Artist,
WindowType.Tag,
+ WindowType.ManageTags,
],
activeTab: 0
})
@@ -134,6 +138,12 @@ export default function MainWindow(props: any) {
dispatch={tabDispatch}
mainDispatch={dispatch}
/>
+ case WindowType.ManageTags:
+ return
default:
throw new Error("Unimplemented window type");
}
diff --git a/client/src/components/appbar/AddTabMenu.tsx b/client/src/components/appbar/AddTabMenu.tsx
index b001fe1..a9e0755 100644
--- a/client/src/components/appbar/AddTabMenu.tsx
+++ b/client/src/components/appbar/AddTabMenu.tsx
@@ -28,5 +28,13 @@ export default function AddTabMenu(props: IProps) {
})
}}
>{WindowType.Query}
+
}
\ No newline at end of file
diff --git a/client/src/components/common/DiscardChangesButton.tsx b/client/src/components/common/DiscardChangesButton.tsx
new file mode 100644
index 0000000..0c6e958
--- /dev/null
+++ b/client/src/components/common/DiscardChangesButton.tsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import { Box, Button } from '@material-ui/core';
+
+export default function DiscardChangesButton(props: any) {
+ return
+
+
+}
\ No newline at end of file
diff --git a/client/src/components/common/MenuEditText.tsx b/client/src/components/common/MenuEditText.tsx
new file mode 100644
index 0000000..5939000
--- /dev/null
+++ b/client/src/components/common/MenuEditText.tsx
@@ -0,0 +1,23 @@
+import React, { useState } from 'react';
+import { TextField } from '@material-ui/core';
+
+export default function MenuEditText(props: {
+ label: string,
+ onSubmit: (s: string) => void,
+}) {
+ const [input, setInput] = useState("");
+
+ return setInput(e.target.value)}
+ onKeyDown={(e: any) => {
+ if (e.key === 'Enter') {
+ // User submitted free-form value.
+ props.onSubmit(input);
+ e.preventDefault();
+ }
+ }}
+ />
+}
\ No newline at end of file
diff --git a/client/src/components/querybuilder/QBSelectWithRequest.tsx b/client/src/components/querybuilder/QBSelectWithRequest.tsx
index dd1b115..107e982 100644
--- a/client/src/components/querybuilder/QBSelectWithRequest.tsx
+++ b/client/src/components/querybuilder/QBSelectWithRequest.tsx
@@ -43,18 +43,6 @@ export default function QBSelectWithRequest(props: IProps & any) {
})();
};
- // // Ensure a new request is made whenever the loading option is enabled.
- // useEffect(() => {
- // startRequest(input);
- // }, []);
-
- // Ensure options are cleared whenever the element is closed.
- // useEffect(() => {
- // if (!open) {
- // setOptions(null);
- // }
- // }, [open]);
-
useEffect(() => {
startRequest(input);
}, [input]);
diff --git a/client/src/components/windows/Windows.tsx b/client/src/components/windows/Windows.tsx
index d5840e5..1411e33 100644
--- a/client/src/components/windows/Windows.tsx
+++ b/client/src/components/windows/Windows.tsx
@@ -1,15 +1,17 @@
import React from 'react';
-import { QueryWindowReducer } from "./QueryWindow";
-import { ArtistWindowReducer } from "./ArtistWindow";
+import { QueryWindowReducer } from "./query/QueryWindow";
+import { ArtistWindowReducer } from "./artist/ArtistWindow";
import SearchIcon from '@material-ui/icons/Search';
import PersonIcon from '@material-ui/icons/Person';
import AlbumIcon from '@material-ui/icons/Album';
import LocalOfferIcon from '@material-ui/icons/LocalOffer';
import AudiotrackIcon from '@material-ui/icons/Audiotrack';
-import { SongWindowReducer } from './SongWindow';
-import { AlbumWindowReducer } from './AlbumWindow';
-import { TagWindowReducer } from './TagWindow';
+import LoyaltyIcon from '@material-ui/icons/Loyalty';
+import { SongWindowReducer } from './song/SongWindow';
+import { AlbumWindowReducer } from './album/AlbumWindow';
+import { TagWindowReducer } from './tag/TagWindow';
import { songGetters } from '../../lib/songGetters';
+import { ManageTagsWindowReducer } from './manage_tags/ManageTagsWindow';
export enum WindowType {
Query = "Query",
@@ -17,6 +19,7 @@ export enum WindowType {
Album = "Album",
Tag = "Tag",
Song = "Song",
+ ManageTags = "ManageTags",
}
export interface WindowState {
@@ -29,6 +32,7 @@ export const newWindowReducer = {
[WindowType.Album]: AlbumWindowReducer,
[WindowType.Song]: SongWindowReducer,
[WindowType.Tag]: TagWindowReducer,
+ [WindowType.ManageTags]: ManageTagsWindowReducer,
}
export const newWindowState = {
@@ -78,4 +82,11 @@ export const newWindowState = {
songsWithTag: null,
}
},
+ [WindowType.ManageTags]: () => {
+ return {
+ tabLabel: <>Manage Tags>,
+ fetchedTags: null,
+ pendingChanges: [],
+ }
+ }
}
\ No newline at end of file
diff --git a/client/src/components/windows/AlbumWindow.tsx b/client/src/components/windows/album/AlbumWindow.tsx
similarity index 71%
rename from client/src/components/windows/AlbumWindow.tsx
rename to client/src/components/windows/album/AlbumWindow.tsx
index 6cefcb8..d879bfc 100644
--- a/client/src/components/windows/AlbumWindow.tsx
+++ b/client/src/components/windows/album/AlbumWindow.tsx
@@ -1,13 +1,15 @@
import React, { useEffect, useState } from 'react';
import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import AlbumIcon from '@material-ui/icons/Album';
-import * as serverApi from '../../api';
-import { WindowState } from './Windows';
-import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon';
-import EditableText from '../common/EditableText';
-import SubmitChangesButton from '../common/SubmitChangesButton';
-import SongTable, { SongGetters } from '../tables/ResultsTable';
-import { saveAlbumChanges } from '../../lib/saveChanges';
+import * as serverApi from '../../../api';
+import { WindowState } from '../Windows';
+import StoreLinkIcon, { whichStore } from '../../common/StoreLinkIcon';
+import EditableText from '../../common/EditableText';
+import SubmitChangesButton from '../../common/SubmitChangesButton';
+import SongTable, { SongGetters } from '../../tables/ResultsTable';
+import { saveAlbumChanges } from '../../../lib/saveChanges';
+import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
+import { queryAlbums, querySongs } from '../../../lib/backend/queries';
var _ = require('lodash');
export type AlbumMetadata = serverApi.AlbumDetails;
@@ -50,38 +52,15 @@ export interface IProps {
}
export async function getAlbumMetadata(id: number) {
- const query = {
- prop: serverApi.QueryElemProperty.albumId,
- propOperand: id,
- propOperator: serverApi.QueryFilterOp.Eq,
- };
-
- var q: serverApi.QueryRequest = {
- query: query,
- offsetsLimits: {
- albumOffset: 0,
- albumLimit: 1,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
+ return (await queryAlbums({
+ query: {
+ a: QueryLeafBy.AlbumId,
+ b: id,
+ leafOp: QueryLeafOp.Equals,
},
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
- return (async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
- let album = json.albums[0];
- return album;
- })();
+ offset: 0,
+ limit: 1,
+ }))[0];
}
export default function AlbumWindow(props: IProps) {
@@ -103,37 +82,20 @@ export default function AlbumWindow(props: IProps) {
useEffect(() => {
if (props.state.songsOnAlbum) { return; }
- var q: serverApi.QueryRequest = {
- query: {
- prop: serverApi.QueryElemProperty.albumId,
- propOperator: serverApi.QueryFilterOp.Eq,
- propOperand: props.state.albumId,
- },
- offsetsLimits: {
- songOffset: 0,
- songLimit: 100,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
- },
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
(async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
+ const songs = await querySongs({
+ query: {
+ a: QueryLeafBy.AlbumId,
+ b: props.state.albumId,
+ leafOp: QueryLeafOp.Equals,
+ },
+ offset: 0,
+ limit: -1,
+ });
props.dispatch({
type: AlbumWindowStateActions.SetSongs,
- value: json.songs,
- });
+ value: songs,
+ });
})();
}, [props.state.songsOnAlbum]);
diff --git a/client/src/components/windows/ArtistWindow.tsx b/client/src/components/windows/artist/ArtistWindow.tsx
similarity index 71%
rename from client/src/components/windows/ArtistWindow.tsx
rename to client/src/components/windows/artist/ArtistWindow.tsx
index 7b8ed4c..fd76abb 100644
--- a/client/src/components/windows/ArtistWindow.tsx
+++ b/client/src/components/windows/artist/ArtistWindow.tsx
@@ -1,13 +1,15 @@
import React, { useEffect, useState } from 'react';
import { Box, Typography, IconButton, Button, CircularProgress } from '@material-ui/core';
import PersonIcon from '@material-ui/icons/Person';
-import * as serverApi from '../../api';
-import { WindowState } from './Windows';
-import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon';
-import EditableText from '../common/EditableText';
-import SubmitChangesButton from '../common/SubmitChangesButton';
-import SongTable, { SongGetters } from '../tables/ResultsTable';
-import { saveArtistChanges } from '../../lib/saveChanges';
+import * as serverApi from '../../../api';
+import { WindowState } from '../Windows';
+import StoreLinkIcon, { whichStore } from '../../common/StoreLinkIcon';
+import EditableText from '../../common/EditableText';
+import SubmitChangesButton from '../../common/SubmitChangesButton';
+import SongTable, { SongGetters } from '../../tables/ResultsTable';
+import { saveArtistChanges } from '../../../lib/saveChanges';
+import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
+import { queryArtists, querySongs } from '../../../lib/backend/queries';
var _ = require('lodash');
export type ArtistMetadata = serverApi.ArtistDetails;
@@ -50,38 +52,15 @@ export interface IProps {
}
export async function getArtistMetadata(id: number) {
- const query = {
- prop: serverApi.QueryElemProperty.artistId,
- propOperand: id,
- propOperator: serverApi.QueryFilterOp.Eq,
- };
-
- var q: serverApi.QueryRequest = {
- query: query,
- offsetsLimits: {
- artistOffset: 0,
- artistLimit: 1,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
+ return (await queryArtists({
+ query: {
+ a: QueryLeafBy.ArtistId,
+ b: id,
+ leafOp: QueryLeafOp.Equals,
},
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
- return (async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
- let artist = json.artists[0];
- return artist;
- })();
+ offset: 0,
+ limit: 1,
+ }))[0];
}
export default function ArtistWindow(props: IProps) {
@@ -103,37 +82,20 @@ export default function ArtistWindow(props: IProps) {
useEffect(() => {
if (props.state.songsByArtist) { return; }
- var q: serverApi.QueryRequest = {
- query: {
- prop: serverApi.QueryElemProperty.artistId,
- propOperator: serverApi.QueryFilterOp.Eq,
- propOperand: props.state.artistId,
- },
- offsetsLimits: {
- songOffset: 0,
- songLimit: 100,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
- },
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
(async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
+ const songs = await querySongs({
+ query: {
+ a: QueryLeafBy.ArtistId,
+ b: props.state.artistId,
+ leafOp: QueryLeafOp.Equals,
+ },
+ offset: 0,
+ limit: -1,
+ });
props.dispatch({
type: ArtistWindowStateActions.SetSongs,
- value: json.songs,
- });
+ value: songs,
+ });
})();
}, [props.state.songsByArtist]);
diff --git a/client/src/components/windows/manage_tags/ManageTagMenu.tsx b/client/src/components/windows/manage_tags/ManageTagMenu.tsx
new file mode 100644
index 0000000..5b5f9a1
--- /dev/null
+++ b/client/src/components/windows/manage_tags/ManageTagMenu.tsx
@@ -0,0 +1,97 @@
+import React, { useState } from 'react';
+import { Menu, MenuItem, TextField, Input } from '@material-ui/core';
+import NestedMenuItem from "material-ui-nested-menu-item";
+import MenuEditText from '../../common/MenuEditText';
+
+export function PickTag(props: {
+ tags: any[]
+ open: boolean
+ root: boolean
+ onPick: (v: string | null) => void
+}) {
+
+ return <>
+ {props.root && }
+ {props.tags.map((tag: any) => {
+ if ('children' in tag && tag.children.length > 0) {
+ return props.onPick(tag.tagId.toString())}
+ >
+
+
+ }
+ return
+ })
+ }>
+}
+
+export default function ManageTagMenu(props: {
+ position: null | number[],
+ open: boolean,
+ onClose: () => void,
+ onRename: (s: string) => void,
+ onDelete: () => void,
+ onMove: (to: string | null) => void,
+ onMergeInto: (to: string) => void,
+ onOpenInTab: () => void,
+ tag: any,
+ changedTags: any[], // Tags organized hierarchically with "children" fields
+}) {
+ const pos = props.open && props.position ?
+ { left: props.position[0], top: props.position[1] }
+ : { left: 0, top: 0 }
+
+ return
+}
\ No newline at end of file
diff --git a/client/src/components/windows/manage_tags/ManageTagsWindow.tsx b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx
new file mode 100644
index 0000000..d9e48ea
--- /dev/null
+++ b/client/src/components/windows/manage_tags/ManageTagsWindow.tsx
@@ -0,0 +1,479 @@
+import React, { useEffect, useState, ReactFragment } from 'react';
+import { WindowState, newWindowReducer, WindowType } from '../Windows';
+import { Box, Typography, Chip, IconButton, useTheme, Button } from '@material-ui/core';
+import LoyaltyIcon from '@material-ui/icons/Loyalty';
+import ArrowRightIcon from '@material-ui/icons/ArrowRight';
+import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
+import ManageTagMenu from './ManageTagMenu';
+import ControlTagChanges, { TagChange, TagChangeType, submitTagChanges } from './TagChange';
+import { queryTags } from '../../../lib/backend/queries';
+import NewTagMenu from './NewTagMenu';
+import { v4 as genUuid } from 'uuid';
+import { MainWindowStateActions } from '../../MainWindow';
+import LocalOfferIcon from '@material-ui/icons/LocalOffer';
+import { songGetters } from '../../../lib/songGetters';
+import Alert from '@material-ui/lab/Alert';
+var _ = require('lodash');
+
+export interface ManageTagsWindowState extends WindowState {
+ // Tags are indexed by a string ID. This can be a stringified MuDBase ID integer,
+ // or a UID for tags which only exist in the front-end and haven't been committed
+ // to the database.
+ fetchedTags: Record | null,
+ pendingChanges: TagChange[],
+ alert: ReactFragment | null, // For notifications such as errors
+}
+
+export enum ManageTagsWindowActions {
+ SetFetchedTags = "SetFetchedTags",
+ SetPendingChanges = "SetPendingChanges",
+ Reset = "Reset",
+ SetAlert = "SetAlert",
+}
+
+export function ManageTagsWindowReducer(state: ManageTagsWindowState, action: any) {
+ switch (action.type) {
+ case ManageTagsWindowActions.SetFetchedTags:
+ return {
+ ...state,
+ fetchedTags: action.value,
+ }
+ case ManageTagsWindowActions.SetPendingChanges:
+ return {
+ ...state,
+ pendingChanges: action.value,
+ }
+ case ManageTagsWindowActions.Reset:
+ return {
+ ...state,
+ pendingChanges: [],
+ fetchedTags: null,
+ alert: null,
+ }
+ case ManageTagsWindowActions.SetAlert:
+ return {
+ ...state,
+ alert: action.value,
+ }
+ default:
+ throw new Error("Unimplemented ManageTagsWindow state update.")
+ }
+}
+
+export function organiseTags(allTags: Record, fromId: string | null): any[] {
+ const base = Object.values(allTags).filter((tag: any) => {
+ var par: any = ("proposedParent" in tag) ? tag.proposedParent : tag.parentId;
+
+ return (fromId === null && !par) ||
+ (par && par === fromId)
+ });
+
+ return base.map((tag: any) => {
+ return {
+ ...tag,
+ children: organiseTags(allTags, tag.tagId),
+ }
+ });
+}
+
+export async function getAllTags() {
+ return (async () => {
+ var retval: Record = {};
+ const tags = await queryTags({
+ query: undefined,
+ offset: 0,
+ limit: -1,
+ });
+ // Convert numeric IDs to string IDs because that is
+ // what we work with within this component.
+ tags.forEach((tag: any) => {
+ retval[tag.tagId.toString()] = {
+ ...tag,
+ tagId: tag.tagId && tag.tagId.toString(),
+ parentId: tag.parentId && tag.parentId.toString(),
+ childIds: tag.childIds && tag.childIds.map((c: number) => c.toString()),
+ }
+ });
+ return retval;
+ })();
+}
+
+export function ExpandArrow(props: {
+ expanded: boolean,
+ onSetExpanded: (v: boolean) => void,
+}) {
+ return props.expanded ?
+ props.onSetExpanded(false)}> :
+ props.onSetExpanded(true)}>;
+}
+
+export function CreateTagButton(props: any) {
+ return
+
+ { }} />
+
+
+
+}
+
+export function SingleTag(props: {
+ tag: any,
+ prependElems: any[],
+ dispatch: (action: any) => void,
+ mainDispatch: (action: any) => void,
+ state: ManageTagsWindowState,
+ changedTags: any[],
+}) {
+ const tag = props.tag;
+ const hasChildren = 'children' in tag && tag.children.length > 0;
+
+ const [menuPos, setMenuPos] = React.useState(null);
+ const [expanded, setExpanded] = useState(false);
+ const theme = useTheme();
+
+ const onOpenMenu = (e: any) => {
+ setMenuPos([e.clientX, e.clientY])
+ };
+ const onCloseMenu = () => {
+ setMenuPos(null);
+ };
+
+ var tagLabel: any = tag.name;
+ if ("proposedName" in tag) {
+ tagLabel = <>{tag.name}→{tag.proposedName}>;
+ } else if ("proposeDelete" in tag && tag.proposeDelete) {
+ tagLabel = <>{tag.name}>;
+ }
+
+ const TagChip = (props: any) =>
+
+ ;
+
+ return <>
+
+
+
+
+ {props.prependElems}
+
+
+ {hasChildren && expanded && tag.children.map((child: any) => ,
+ /]}
+ dispatch={props.dispatch}
+ mainDispatch={props.mainDispatch}
+ state={props.state}
+ changedTags={props.changedTags}
+ />)}
+ {
+ props.mainDispatch({
+ type: MainWindowStateActions.AddTab,
+ tabState: {
+ tabLabel: <>{tag.name}>,
+ tagId: tag.tagId,
+ metadata: null,
+ songGetters: songGetters,
+ songsWithTag: null,
+ },
+ tabReducer: newWindowReducer[WindowType.Tag],
+ tabType: WindowType.Tag,
+ })
+ }}
+ onRename={(s: string) => {
+ props.dispatch({
+ type: ManageTagsWindowActions.SetPendingChanges,
+ value: [
+ ...props.state.pendingChanges,
+ {
+ type: TagChangeType.Rename,
+ name: s,
+ id: tag.tagId,
+ }
+ ]
+ })
+ props.dispatch({
+ type: ManageTagsWindowActions.SetAlert,
+ value: null,
+ })
+ }}
+ onDelete={() => {
+ props.dispatch({
+ type: ManageTagsWindowActions.SetPendingChanges,
+ value: [
+ ...props.state.pendingChanges,
+ {
+ type: TagChangeType.Delete,
+ id: tag.tagId,
+ }
+ ]
+ })
+ props.dispatch({
+ type: ManageTagsWindowActions.SetAlert,
+ value: null,
+ })
+ }}
+ onMove={(to: string | null) => {
+ props.dispatch({
+ type: ManageTagsWindowActions.SetPendingChanges,
+ value: [
+ ...props.state.pendingChanges,
+ {
+ type: TagChangeType.MoveTo,
+ id: tag.tagId,
+ parent: to,
+ }
+ ]
+ })
+ props.dispatch({
+ type: ManageTagsWindowActions.SetAlert,
+ value: null,
+ })
+ }}
+ onMergeInto={(into: string) => {
+ props.dispatch({
+ type: ManageTagsWindowActions.SetPendingChanges,
+ value: [
+ ...props.state.pendingChanges,
+ {
+ type: TagChangeType.MergeTo,
+ id: tag.tagId,
+ into: into,
+ }
+ ]
+ })
+ props.dispatch({
+ type: ManageTagsWindowActions.SetAlert,
+ value: null,
+ })
+ }}
+ tag={tag}
+ changedTags={props.changedTags}
+ />
+ >
+}
+
+function annotateTagsWithChanges(tags: Record, changes: TagChange[]) {
+ var retval: Record = _.cloneDeep(tags);
+
+ const applyDelete = (id: string) => {
+ retval[id].proposeDelete = true;
+ Object.values(tags).filter((t: any) => t.parentId === id)
+ .forEach((child: any) => applyDelete(child.tagId));
+ }
+
+ changes.forEach((change: TagChange) => {
+ switch (change.type) {
+ case TagChangeType.Rename:
+ retval[change.id].proposedName = change.name;
+ break;
+ case TagChangeType.Delete:
+ applyDelete(change.id);
+ break;
+ case TagChangeType.MoveTo:
+ retval[change.id].proposedParent = change.parent;
+ break;
+ case TagChangeType.MergeTo:
+ retval[change.id].proposedMergeInto = change.into;
+ break;
+ case TagChangeType.Create:
+ retval[change.id] = {
+ isNewTag: true,
+ name: change.name,
+ parentId: change.parent,
+ tagId: change.id,
+ }
+ if (change.parent) {
+ retval[change.parent].childIds =
+ [...retval[change.parent].childIds, change.id]
+ }
+ break;
+ default:
+ throw new Error("Unimplemented tag change")
+ }
+ })
+ return retval;
+}
+
+function applyTagsChanges(tags: Record, changes: TagChange[]) {
+ var retval = _.cloneDeep(tags);
+
+ const applyDelete = (id: string) => {
+ Object.values(tags).filter((t: any) => t.parentId === id)
+ .forEach((child: any) => applyDelete(child.tagId));
+ delete retval[id].proposeDelete;
+ }
+
+ changes.forEach((change: TagChange) => {
+ switch (change.type) {
+ case TagChangeType.Rename:
+ retval[change.id].name = change.name;
+ break;
+ case TagChangeType.Delete:
+ applyDelete(change.id);
+ break;
+ case TagChangeType.MoveTo:
+ retval[change.id].parentId = change.parent;
+ if (change.parent === null) { delete retval[change.id].parentId; }
+ break;
+ case TagChangeType.MergeTo:
+ applyDelete(change.id);
+ break;
+ case TagChangeType.Create:
+ retval[change.id] = {
+ name: change.name,
+ tagId: change.id,
+ parentId: change.parent,
+ isNewTag: true,
+ }
+ if (change.parent) {
+ retval[change.parent].childIds =
+ [...retval[change.parent].childIds, change.id]
+ }
+ break;
+ default:
+ throw new Error("Unimplemented tag change")
+ }
+ })
+ return retval;
+}
+
+export default function ManageTagsWindow(props: {
+ state: ManageTagsWindowState,
+ dispatch: (action: any) => void,
+ mainDispatch: (action: any) => void,
+}) {
+ const [newTagMenuPos, setNewTagMenuPos] = React.useState(null);
+
+ const onOpenNewTagMenu = (e: any) => {
+ setNewTagMenuPos([e.clientX, e.clientY])
+ };
+ const onCloseNewTagMenu = () => {
+ setNewTagMenuPos(null);
+ };
+
+ useEffect(() => {
+ if (props.state.fetchedTags !== null) {
+ return;
+ }
+ (async () => {
+ const allTags = await getAllTags();
+ // We have the tags in list form. Now, we want to organize
+ // them hierarchically by giving each tag a "children" prop.
+ props.dispatch({
+ type: ManageTagsWindowActions.SetFetchedTags,
+ value: allTags,
+ });
+ })();
+ }, [props.state.fetchedTags]);
+
+ const tagsWithChanges = annotateTagsWithChanges(props.state.fetchedTags || {}, props.state.pendingChanges)
+ const changedTags = organiseTags(
+ applyTagsChanges(props.state.fetchedTags || {}, props.state.pendingChanges),
+ null);
+ const tags = organiseTags(tagsWithChanges, null);
+
+ return <>
+
+
+
+
+
+ Manage Tags
+
+ {props.state.pendingChanges.length > 0 &&
+ {
+ props.dispatch({
+ type: ManageTagsWindowActions.SetPendingChanges,
+ value: [],
+ })
+ props.dispatch({
+ type: ManageTagsWindowActions.SetAlert,
+ value: null,
+ })
+ }}
+ onSave={() => {
+ submitTagChanges(props.state.pendingChanges).then(() => {
+ props.dispatch({
+ type: ManageTagsWindowActions.Reset
+ });
+ }).catch((e: Error) => {
+ props.dispatch({
+ type: ManageTagsWindowActions.SetAlert,
+ value: Failed to save changes: {e.message},
+ })
+ })
+ }}
+ getTagDetails={(id: string) => tagsWithChanges[id]}
+ />
+ }
+ {props.state.alert && {props.state.alert}}
+
+ {tags && tags.length && tags.map((tag: any) => {
+ return ;
+ })}
+ { onOpenNewTagMenu(e) }} />
+
+
+ {
+ props.dispatch({
+ type: ManageTagsWindowActions.SetPendingChanges,
+ value: [
+ ...props.state.pendingChanges,
+ {
+ type: TagChangeType.Create,
+ id: genUuid(),
+ parent: parentId,
+ name: name,
+ }
+ ]
+ })
+ }}
+ onClose={onCloseNewTagMenu}
+ changedTags={changedTags}
+ />
+ >
+}
\ No newline at end of file
diff --git a/client/src/components/windows/manage_tags/NewTagMenu.tsx b/client/src/components/windows/manage_tags/NewTagMenu.tsx
new file mode 100644
index 0000000..3d16315
--- /dev/null
+++ b/client/src/components/windows/manage_tags/NewTagMenu.tsx
@@ -0,0 +1,54 @@
+import React, { useState } from 'react';
+import { Menu, MenuItem, TextField, Input } from '@material-ui/core';
+import NestedMenuItem from "material-ui-nested-menu-item";
+import MenuEditText from '../../common/MenuEditText';
+
+export function PickCreateTag(props: {
+ tags: any[],
+ open: boolean,
+ parentId: string | null,
+ onCreate: (name: string, parentId: string | null) => void,
+}) {
+
+ return <>
+ {
+ props.onCreate(s, props.parentId);
+ }}
+ />
+ {props.tags.map((tag: any) => {
+ return
+
+
+ })}
+ >
+}
+
+export default function NewTagMenu(props: {
+ position: null | number[],
+ open: boolean,
+ onCreate: (name: string, parentId: string | null) => void,
+ onClose: () => void,
+ changedTags: any[], // Tags organized hierarchically with "children" fields
+}) {
+ const pos = props.open && props.position ?
+ { left: props.position[0], top: props.position[1] }
+ : { left: 0, top: 0 }
+
+ return
+}
\ No newline at end of file
diff --git a/client/src/components/windows/manage_tags/TagChange.tsx b/client/src/components/windows/manage_tags/TagChange.tsx
new file mode 100644
index 0000000..cce1c06
--- /dev/null
+++ b/client/src/components/windows/manage_tags/TagChange.tsx
@@ -0,0 +1,147 @@
+import React, { useState, useEffect } from 'react';
+import { Typography, Chip, CircularProgress, Box, Paper } from '@material-ui/core';
+import { queryTags } from '../../../lib/backend/queries';
+import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
+import DiscardChangesButton from '../../common/DiscardChangesButton';
+import SubmitChangesButton from '../../common/SubmitChangesButton';
+import { createTag, modifyTag, deleteTag, mergeTag } from '../../../lib/backend/tags';
+
+export enum TagChangeType {
+ Delete = "Delete",
+ Create = "Create",
+ MoveTo = "MoveTo",
+ MergeTo = "MergeTo",
+ Rename = "Rename",
+}
+
+export interface TagChange {
+ type: TagChangeType,
+ id: string, // Stringified integer == MuDBase ID. Other string == not yet committed to DB.
+ parent?: string | null, // Stringified integer == MuDBase ID. Other string == not yet committed to DB.
+ // null refers to the tags root.
+ name?: string,
+ into?: string, // Used for storing the tag ID to merge into, if applicable. As in the other ID fields.
+}
+
+export async function submitTagChanges(changes: TagChange[]) {
+ // Upon entering this function, some tags have a real numeric MuDBase ID (stringified),
+ // while others have a UUID string which is a placeholder until the tag is created.
+ // While applying the changes, UUIDs will be replaced by real numeric IDs.
+ // Therefore we maintain a lookup table for mapping the old to the new.
+ var id_lookup: Record = {}
+
+ const getId = (id_string: string) => {
+ return (Number(id_string) === NaN) ?
+ id_lookup[id_string] : Number(id_string);
+ }
+
+ for (const change of changes) {
+ // If string is of form "1", convert to ID number directly.
+ // Otherwise, look it up in the table.
+ const parentId = change.parent ? getId(change.parent) : undefined;
+ const numericId = change.id ? getId(change.id) : undefined;
+ const intoId = change.into ? getId(change.into) : undefined;
+ switch (change.type) {
+ case TagChangeType.Create:
+ if (!change.name) { throw new Error("Cannot create tag without name"); }
+ const { id } = await createTag({
+ name: change.name,
+ parentId: parentId,
+ });
+ id_lookup[change.id] = id;
+ break;
+ case TagChangeType.MoveTo:
+ if (!numericId) { throw new Error("Cannot modify tag with no numeric ID"); }
+ await modifyTag(
+ numericId,
+ {
+ parentId: parentId,
+ })
+ break;
+ case TagChangeType.Rename:
+ if (!numericId) { throw new Error("Cannot modify tag with no numeric ID"); }
+ await modifyTag(
+ numericId,
+ {
+ name: change.name,
+ })
+ break;
+ case TagChangeType.Delete:
+ if (!numericId) { throw new Error("Cannot delete tag with no numeric ID"); }
+ await deleteTag(numericId)
+ break;
+ case TagChangeType.MergeTo:
+ if (!numericId) { throw new Error("Cannot merge tag with no numeric ID"); }
+ if (!intoId) { throw new Error("Cannot merge tag into tag with no numeric ID"); }
+ await mergeTag(numericId, intoId);
+ break;
+ default:
+ throw new Error("Unimplemented tag change");
+ }
+ }
+}
+
+export function TagChangeDisplay(props: {
+ change: TagChange,
+ getTagDetails: (id: string) => any,
+}) {
+ const tag = props.getTagDetails(props.change.id);
+ const oldParent = tag.parentId ? props.getTagDetails(tag.parentId) : null;
+ const newParent = props.change.parent ? props.getTagDetails(props.change.parent) : null;
+
+ const MakeTag = (props: { name: string }) =>
+ const MainTag = tag ?
+ :
+ ;
+
+ switch (props.change.type) {
+ case TagChangeType.Delete:
+ return Delete {MainTag}
+ case TagChangeType.Rename:
+ const NewTag = tag ?
+ :
+ ;
+ return Rename {MainTag} to {NewTag}
+ case TagChangeType.MoveTo:
+ const OldParent = oldParent !== undefined ?
+ :
+ ;
+ const NewParent = newParent !== undefined ?
+ :
+ ;
+ return Move {MainTag} from {OldParent} to {NewParent}
+ case TagChangeType.MergeTo:
+ const intoTag = props.getTagDetails(props.change.into || "unknown");
+ const IntoTag =
+ return Merge {MainTag} into {IntoTag}
+ case TagChangeType.Create:
+ return props.change.parent ?
+ Create {MainTag} under :
+ Create {MainTag}
+ default:
+ throw new Error("Unhandled tag change type")
+ }
+}
+
+export default function ControlTagChanges(props: {
+ changes: TagChange[],
+ onSave: () => void,
+ onDiscard: () => void,
+ getTagDetails: (id: string) => any,
+}) {
+ return
+ Pending changes
+
+ {props.changes.map((change: any) =>
+
+ -
+
+
+ )}
+
+
+ Save Changes
+ Discard Changes
+
+
+}
\ No newline at end of file
diff --git a/client/src/components/windows/QueryWindow.tsx b/client/src/components/windows/query/QueryWindow.tsx
similarity index 59%
rename from client/src/components/windows/QueryWindow.tsx
rename to client/src/components/windows/query/QueryWindow.tsx
index e35d233..b410a9d 100644
--- a/client/src/components/windows/QueryWindow.tsx
+++ b/client/src/components/windows/query/QueryWindow.tsx
@@ -1,13 +1,13 @@
import React, { useEffect } from 'react';
import { createMuiTheme, Box, LinearProgress } from '@material-ui/core';
-import { QueryElem, toApiQuery } from '../../lib/query/Query';
-import QueryBuilder from '../querybuilder/QueryBuilder';
-import * as serverApi from '../../api';
-import SongTable from '../tables/ResultsTable';
-import { songGetters } from '../../lib/songGetters';
-import { getArtists, getSongTitles, getAlbums, getTags } from '../../lib/query/Getters';
+import { QueryElem, toApiQuery, QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
+import QueryBuilder from '../../querybuilder/QueryBuilder';
+import * as serverApi from '../../../api';
+import SongTable from '../../tables/ResultsTable';
+import { songGetters } from '../../../lib/songGetters';
+import { queryArtists, querySongs, queryAlbums, queryTags } from '../../../lib/backend/queries';
import { grey } from '@material-ui/core/colors';
-import { WindowState } from './Windows';
+import { WindowState } from '../Windows';
var _ = require('lodash');
const darkTheme = createMuiTheme({
@@ -36,6 +36,56 @@ export enum QueryWindowStateActions {
SetResultsForQuery = "setResultsForQuery",
}
+async function getArtistNames(filter: string) {
+ const artists = await queryArtists({
+ query: filter.length > 0 ? {
+ a: QueryLeafBy.ArtistName,
+ b: '%' + filter + '%',
+ leafOp: QueryLeafOp.Like
+ } : undefined,
+ offset: 0,
+ limit: -1,
+ });
+
+ return [...(new Set([...(artists.map((a:any) => a.name))]))];
+}
+
+async function getAlbumNames(filter: string) {
+ const albums = await queryAlbums({
+ query: filter.length > 0 ? {
+ a: QueryLeafBy.AlbumName,
+ b: '%' + filter + '%',
+ leafOp: QueryLeafOp.Like
+ } : undefined,
+ offset: 0,
+ limit: -1,
+ });
+
+ return [...(new Set([...(albums.map((a:any) => a.name))]))];
+}
+
+async function getSongTitles(filter: string) {
+ const songs = await querySongs({
+ query: filter.length > 0 ? {
+ a: QueryLeafBy.SongTitle,
+ b: '%' + filter + '%',
+ leafOp: QueryLeafOp.Like
+ } : undefined,
+ offset: 0,
+ limit: -1,
+ });
+
+ return [...(new Set([...(songs.map((s:any) => s.title))]))];
+}
+
+async function getTagItems() {
+ return await queryTags({
+ query: undefined,
+ offset: 0,
+ limit: -1,
+ });
+}
+
export function QueryWindowReducer(state: QueryWindowState, action: any) {
switch (action.type) {
case QueryWindowStateActions.SetQuery:
@@ -73,36 +123,18 @@ export default function QueryWindow(props: IProps) {
const showResults = (query && resultsFor && query == resultsFor.for) ? resultsFor.results : [];
const doQuery = async (_query: QueryElem) => {
- var q: serverApi.QueryRequest = {
- query: toApiQuery(_query),
- offsetsLimits: {
- songOffset: 0,
- songLimit: 100,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
- },
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
- return (async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
- if (_.isEqual(query, _query)) {
- setResultsForQuery({
- for: _query,
- results: json.songs,
- })
- }
- })();
+ const songs = await querySongs({
+ query: _query,
+ offset: 0,
+ limit: 100, //TODO: pagination
+ });
+
+ if (_.isEqual(query, _query)) {
+ setResultsForQuery({
+ for: _query,
+ results: songs,
+ })
+ }
}
useEffect(() => {
@@ -124,10 +156,10 @@ export default function QueryWindow(props: IProps) {
editing={editing}
onChangeEditing={setEditingQuery}
requestFunctions={{
- getArtists: getArtists,
+ getArtists: getArtistNames,
getSongTitles: getSongTitles,
- getAlbums: getAlbums,
- getTags: getTags,
+ getAlbums: getAlbumNames,
+ getTags: getTagItems,
}}
/>
diff --git a/client/src/components/windows/SongWindow.tsx b/client/src/components/windows/song/SongWindow.tsx
similarity index 80%
rename from client/src/components/windows/SongWindow.tsx
rename to client/src/components/windows/song/SongWindow.tsx
index bf69c84..e7048aa 100644
--- a/client/src/components/windows/SongWindow.tsx
+++ b/client/src/components/windows/song/SongWindow.tsx
@@ -3,14 +3,16 @@ import { Box, Typography, IconButton, Button, CircularProgress } from '@material
import AudiotrackIcon from '@material-ui/icons/Audiotrack';
import PersonIcon from '@material-ui/icons/Person';
import AlbumIcon from '@material-ui/icons/Album';
-import * as serverApi from '../../api';
-import { WindowState } from './Windows';
-import { ArtistMetadata } from './ArtistWindow';
-import { AlbumMetadata } from './AlbumWindow';
-import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon';
-import EditableText from '../common/EditableText';
-import SubmitChangesButton from '../common/SubmitChangesButton';
-import { saveSongChanges } from '../../lib/saveChanges';
+import * as serverApi from '../../../api';
+import { WindowState } from '../Windows';
+import { ArtistMetadata } from '../artist/ArtistWindow';
+import { AlbumMetadata } from '../album/AlbumWindow';
+import StoreLinkIcon, { whichStore } from '../../common/StoreLinkIcon';
+import EditableText from '../../common/EditableText';
+import SubmitChangesButton from '../../common/SubmitChangesButton';
+import { saveSongChanges } from '../../../lib/saveChanges';
+import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
+import { querySongs } from '../../../lib/backend/queries';
export type SongMetadata = serverApi.SongDetails;
export type SongMetadataChanges = serverApi.ModifySongRequest;
@@ -47,38 +49,15 @@ export interface IProps {
}
export async function getSongMetadata(id: number) {
- const query = {
- prop: serverApi.QueryElemProperty.songId,
- propOperand: id,
- propOperator: serverApi.QueryFilterOp.Eq,
- };
-
- var q: serverApi.QueryRequest = {
- query: query,
- offsetsLimits: {
- songOffset: 0,
- songLimit: 1,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
+ return (await querySongs({
+ query: {
+ a: QueryLeafBy.SongId,
+ b: id,
+ leafOp: QueryLeafOp.Equals,
},
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
- return (async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
- let song = json.songs[0];
- return song;
- })();
+ offset: 0,
+ limit: 1,
+ }))[0];
}
export default function SongWindow(props: IProps) {
diff --git a/client/src/components/windows/TagWindow.tsx b/client/src/components/windows/tag/TagWindow.tsx
similarity index 70%
rename from client/src/components/windows/TagWindow.tsx
rename to client/src/components/windows/tag/TagWindow.tsx
index 676aef5..ec33f91 100644
--- a/client/src/components/windows/TagWindow.tsx
+++ b/client/src/components/windows/tag/TagWindow.tsx
@@ -1,13 +1,15 @@
import React, { useEffect, useState } from 'react';
import { Box, Typography, IconButton, CircularProgress } from '@material-ui/core';
import LocalOfferIcon from '@material-ui/icons/LocalOffer';
-import * as serverApi from '../../api';
-import { WindowState } from './Windows';
-import StoreLinkIcon, { whichStore } from '../common/StoreLinkIcon';
-import EditableText from '../common/EditableText';
-import SubmitChangesButton from '../common/SubmitChangesButton';
-import SongTable, { SongGetters } from '../tables/ResultsTable';
-import { saveTagChanges } from '../../lib/saveChanges';
+import * as serverApi from '../../../api';
+import { WindowState } from '../Windows';
+import StoreLinkIcon, { whichStore } from '../../common/StoreLinkIcon';
+import EditableText from '../../common/EditableText';
+import SubmitChangesButton from '../../common/SubmitChangesButton';
+import SongTable, { SongGetters } from '../../tables/ResultsTable';
+import { saveTagChanges } from '../../../lib/saveChanges';
+import { queryTags, querySongs } from '../../../lib/backend/queries';
+import { QueryLeafBy, QueryLeafOp } from '../../../lib/query/Query';
var _ = require('lodash');
export interface FullTagMetadata extends serverApi.TagDetails {
@@ -55,49 +57,27 @@ export interface IProps {
}
export async function getTagMetadata(id: number) {
- const query = {
- prop: serverApi.QueryElemProperty.tagId,
- propOperand: id,
- propOperator: serverApi.QueryFilterOp.Eq,
- };
-
- var q: serverApi.QueryRequest = {
- query: query,
- offsetsLimits: {
- tagOffset: 0,
- tagLimit: 1,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
+ var tag = (await queryTags({
+ query: {
+ a: QueryLeafBy.TagId,
+ b: id,
+ leafOp: QueryLeafOp.Equals,
},
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
- return (async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
- let tag = json.tags[0];
-
- // Recursively fetch parent tags to build the full metadata.
- if (tag.parentId) {
- const parent = await getTagMetadata(tag.parentId);
- tag.fullName = [...parent.fullName, tag.name];
- tag.fullId = [...parent.fullId, tag.tagId];
- } else {
- tag.fullName = [tag.name];
- tag.fullId = [tag.tagId];
- }
+ offset: 0,
+ limit: 1,
+ }))[0];
+
+ // Recursively fetch parent tags to build the full metadata.
+ if (tag.parentId) {
+ const parent = await getTagMetadata(tag.parentId);
+ tag.fullName = [...parent.fullName, tag.name];
+ tag.fullId = [...parent.fullId, tag.tagId];
+ } else {
+ tag.fullName = [tag.name];
+ tag.fullId = [tag.tagId];
+ }
- return tag;
- })();
+ return tag;
}
export default function TagWindow(props: IProps) {
@@ -119,37 +99,20 @@ export default function TagWindow(props: IProps) {
useEffect(() => {
if (props.state.songsWithTag) { return; }
- var q: serverApi.QueryRequest = {
- query: {
- prop: serverApi.QueryElemProperty.tagId,
- propOperator: serverApi.QueryFilterOp.Eq,
- propOperand: props.state.tagId,
- },
- offsetsLimits: {
- songOffset: 0,
- songLimit: 100,
- },
- ordering: {
- orderBy: {
- type: serverApi.OrderByType.Name,
- },
- ascending: true,
- },
- };
-
- const requestOpts = {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(q),
- };
-
(async () => {
- const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
- let json: any = await response.json();
+ const songs = await querySongs({
+ query: {
+ a: QueryLeafBy.TagId,
+ b: props.state.tagId,
+ leafOp: QueryLeafOp.Equals,
+ },
+ offset: 0,
+ limit: -1,
+ });
props.dispatch({
type: TagWindowStateActions.SetSongs,
- value: json.songs,
- });
+ value: songs,
+ });
})();
}, [props.state.songsWithTag]);
diff --git a/client/src/lib/query/Getters.tsx b/client/src/lib/backend/queries.tsx
similarity index 67%
rename from client/src/lib/query/Getters.tsx
rename to client/src/lib/backend/queries.tsx
index 8d16129..d93ef40 100644
--- a/client/src/lib/query/Getters.tsx
+++ b/client/src/lib/backend/queries.tsx
@@ -1,17 +1,18 @@
import * as serverApi from '../../api';
+import { QueryElem, toApiQuery } from '../query/Query';
-export async function getArtists(filter: string) {
- const query = filter.length > 0 ? {
- prop: serverApi.QueryElemProperty.artistName,
- propOperand: filter,
- propOperator: serverApi.QueryFilterOp.Like,
- } : {};
+export interface QueryArgs {
+ query?: QueryElem,
+ offset: number,
+ limit: number,
+}
+export async function queryArtists(args: QueryArgs) {
var q: serverApi.QueryRequest = {
- query: query,
+ query: args.query ? toApiQuery(args.query) : {},
offsetsLimits: {
- artistOffset: 0,
- artistLimit: 100,
+ artistOffset: args.offset,
+ artistLimit: args.limit,
},
ordering: {
orderBy: {
@@ -30,23 +31,16 @@ export async function getArtists(filter: string) {
return (async () => {
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
let json: any = await response.json();
- const names: string[] = json.artists.map((elem: any) => { return elem.name; });
- return [...new Set(names)];
+ return json.artists;
})();
}
-export async function getAlbums(filter: string) {
- const query = filter.length > 0 ? {
- prop: serverApi.QueryElemProperty.albumName,
- propOperand: filter,
- propOperator: serverApi.QueryFilterOp.Like,
- } : {};
-
+export async function queryAlbums(args: QueryArgs) {
var q: serverApi.QueryRequest = {
- query: query,
+ query: args.query ? toApiQuery(args.query) : {},
offsetsLimits: {
- albumOffset: 0,
- albumLimit: 100,
+ albumOffset: args.offset,
+ albumLimit: args.limit,
},
ordering: {
orderBy: {
@@ -65,23 +59,16 @@ export async function getAlbums(filter: string) {
return (async () => {
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
let json: any = await response.json();
- const names: string[] = json.albums.map((elem: any) => { return elem.name; });
- return [...new Set(names)];
+ return json.albums;
})();
}
-export async function getSongTitles(filter: string) {
- const query = filter.length > 0 ? {
- prop: serverApi.QueryElemProperty.songTitle,
- propOperand: filter,
- propOperator: serverApi.QueryFilterOp.Like,
- } : {};
-
+export async function querySongs(args: QueryArgs) {
var q: serverApi.QueryRequest = {
- query: query,
+ query: args.query ? toApiQuery(args.query) : {},
offsetsLimits: {
- songOffset: 0,
- songLimit: 100,
+ songOffset: args.offset,
+ songLimit: args.limit,
},
ordering: {
orderBy: {
@@ -100,17 +87,16 @@ export async function getSongTitles(filter: string) {
return (async () => {
const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.QueryEndpoint, requestOpts)
let json: any = await response.json();
- const titles: string[] = json.songs.map((elem: any) => { return elem.title; });
- return [...new Set(titles)];
+ return json.songs;
})();
}
-export async function getTags() {
+export async function queryTags(args: QueryArgs) {
var q: serverApi.QueryRequest = {
- query: {},
+ query: args.query ? toApiQuery(args.query) : {},
offsetsLimits: {
- tagOffset: 0,
- tagLimit: 100,
+ tagOffset: args.offset,
+ tagLimit: args.limit,
},
ordering: {
orderBy: {
diff --git a/client/src/lib/backend/tags.tsx b/client/src/lib/backend/tags.tsx
new file mode 100644
index 0000000..67146e9
--- /dev/null
+++ b/client/src/lib/backend/tags.tsx
@@ -0,0 +1,61 @@
+import * as serverApi from '../../api';
+
+export async function createTag(details: serverApi.CreateTagRequest) {
+ const requestOpts = {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(details),
+ };
+
+ const response = await fetch((process.env.REACT_APP_BACKEND || "") + serverApi.CreateTagEndpoint, requestOpts)
+ if (!response.ok) {
+ throw new Error("Response to tag creation not OK: " + JSON.stringify(response));
+ }
+ return await response.json();
+}
+
+export async function modifyTag(id: number, details: serverApi.ModifyTagRequest) {
+ const requestOpts = {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(details),
+ };
+
+ const response = await fetch(
+ (process.env.REACT_APP_BACKEND || "") + serverApi.ModifyTagEndpoint.replace(':id', id.toString()),
+ requestOpts
+ );
+ if (!response.ok) {
+ throw new Error("Response to tag modification not OK: " + JSON.stringify(response));
+ }
+}
+
+export async function deleteTag(id: number) {
+ const requestOpts = {
+ method: 'DELETE',
+ };
+
+ const response = await fetch(
+ (process.env.REACT_APP_BACKEND || "") + serverApi.DeleteTagEndpoint.replace(':id', id.toString()),
+ requestOpts
+ );
+ if (!response.ok) {
+ throw new Error("Response to tag deletion not OK: " + JSON.stringify(response));
+ }
+}
+
+export async function mergeTag(fromId: number, toId: number) {
+ const requestOpts = {
+ method: 'POST',
+ };
+
+ const response = await fetch(
+ (process.env.REACT_APP_BACKEND || "") + serverApi.MergeTagEndpoint
+ .replace(':id', fromId.toString())
+ .replace(':toId', toId.toString()),
+ requestOpts
+ );
+ if (!response.ok) {
+ throw new Error("Response to tag merge not OK: " + JSON.stringify(response));
+ }
+}
\ No newline at end of file
diff --git a/client/src/lib/query/Query.tsx b/client/src/lib/query/Query.tsx
index 9109cfd..225d118 100644
--- a/client/src/lib/query/Query.tsx
+++ b/client/src/lib/query/Query.tsx
@@ -2,9 +2,13 @@ import * as serverApi from '../../api';
export enum QueryLeafBy {
ArtistName = 0,
+ ArtistId,
AlbumName,
+ AlbumId,
TagInfo,
- SongTitle
+ TagId,
+ SongTitle,
+ SongId,
}
export enum QueryLeafOp {
@@ -14,11 +18,11 @@ export enum QueryLeafOp {
}
export interface TagQueryInfo {
- fullName: string[],
matchIds: number[],
+ fullName: string[],
}
export function isTagQueryInfo(e: any): e is TagQueryInfo {
- return (typeof e === 'object') && 'fullName' in e && 'matchIds' in e;
+ return (typeof e === 'object') && 'matchIds' in e && 'fullName' in e;
}
export type QueryLeafOperand = string | number | TagQueryInfo;
@@ -166,6 +170,10 @@ export function toApiQuery(q: QueryElem) : serverApi.Query {
[QueryLeafBy.SongTitle]: serverApi.QueryElemProperty.songTitle,
[QueryLeafBy.ArtistName]: serverApi.QueryElemProperty.artistName,
[QueryLeafBy.AlbumName]: serverApi.QueryElemProperty.albumName,
+ [QueryLeafBy.AlbumId]: serverApi.QueryElemProperty.albumId,
+ [QueryLeafBy.ArtistId]: serverApi.QueryElemProperty.artistId,
+ [QueryLeafBy.TagId]: serverApi.QueryElemProperty.tagId,
+ [QueryLeafBy.SongId]: serverApi.QueryElemProperty.songId,
}
const leafOpsMapping: any = {
[QueryLeafOp.Equals]: serverApi.QueryFilterOp.Eq,
diff --git a/server/app.ts b/server/app.ts
index 8d1922a..3155d73 100644
--- a/server/app.ts
+++ b/server/app.ts
@@ -15,6 +15,8 @@ import { TagDetailsEndpointHandler } from './endpoints/TagDetailsEndpointHandler
import { CreateAlbumEndpointHandler } from './endpoints/CreateAlbumEndpointHandler';
import { ModifyAlbumEndpointHandler } from './endpoints/ModifyAlbumEndpointHandler';
import { AlbumDetailsEndpointHandler } from './endpoints/AlbumDetailsEndpointHandler';
+import { DeleteTagEndpointHandler } from './endpoints/DeleteTagEndpointHandler';
+import { MergeTagEndpointHandler } from './endpoints/MergeTagEndpointHandler';
import * as endpointTypes from './endpoints/types';
const invokeHandler = (handler:endpointTypes.EndpointHandler, knex: Knex) => {
@@ -53,6 +55,8 @@ const SetupApp = (app: any, knex: Knex, apiBaseUrl: string) => {
app.post(apiBaseUrl + api.CreateAlbumEndpoint, invokeWithKnex(CreateAlbumEndpointHandler));
app.put(apiBaseUrl + api.ModifyAlbumEndpoint, invokeWithKnex(ModifyAlbumEndpointHandler));
app.get(apiBaseUrl + api.AlbumDetailsEndpoint, invokeWithKnex(AlbumDetailsEndpointHandler));
+ app.delete(apiBaseUrl + api.DeleteTagEndpoint, invokeWithKnex(DeleteTagEndpointHandler));
+ app.post(apiBaseUrl + api.MergeTagEndpoint, invokeWithKnex(MergeTagEndpointHandler));
}
export { SetupApp }
\ No newline at end of file
diff --git a/server/endpoints/DeleteTagEndpointHandler.ts b/server/endpoints/DeleteTagEndpointHandler.ts
new file mode 100644
index 0000000..6d0ab94
--- /dev/null
+++ b/server/endpoints/DeleteTagEndpointHandler.ts
@@ -0,0 +1,76 @@
+import * as api from '../../client/src/api';
+import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types';
+import Knex from 'knex';
+
+async function getChildrenRecursive(id: number, trx: any) {
+ const directChildren = (await trx.select('id')
+ .from('tags')
+ .where({ 'parentId': id })).map((r: any) => r.id);
+
+ const indirectChildrenPromises = directChildren.map(
+ (child: number) => getChildrenRecursive(child, trx)
+ );
+ const indirectChildrenNested = await Promise.all(indirectChildrenPromises);
+ const indirectChildren = indirectChildrenNested.flat();
+
+ return [
+ ...directChildren,
+ ...indirectChildren,
+ ]
+}
+
+export const DeleteTagEndpointHandler: EndpointHandler = async (req: any, res: any, knex: Knex) => {
+ if (!api.checkDeleteTagRequest(req)) {
+ const e: EndpointError = {
+ internalMessage: 'Invalid DeleteTag request: ' + JSON.stringify(req.body),
+ httpStatus: 400
+ };
+ throw e;
+ }
+ const reqObject: api.DeleteTagRequest = req.body;
+
+ console.log("Delete Tag:", reqObject);
+
+ await knex.transaction(async (trx) => {
+ try {
+ // Start retrieving any child tags.
+
+ const childTagsPromise =
+ getChildrenRecursive(req.params.id, trx);
+
+ // Start retrieving the tag itself.
+ const tagPromise = trx.select('id')
+ .from('tags')
+ .where({ id: req.params.id })
+ .then((r: any) => (r && r[0]) ? r[0]['id'] : undefined)
+
+ // Wait for the requests to finish.
+ var [tag, children] = await Promise.all([tagPromise, childTagsPromise]);
+
+ // Merge all IDs.
+ const toDelete = [ tag, ...children ];
+ console.log ("deleting tags: ", toDelete);
+
+ // Check that we found all objects we need.
+ if (!tag) {
+ const e: EndpointError = {
+ internalMessage: 'Tag or parent does not exist for DeleteTag request: ' + JSON.stringify(req.body),
+ httpStatus: 400
+ };
+ throw e;
+ }
+
+ // Delete the tag and its children.
+ await trx('tags')
+ .whereIn('id', toDelete)
+ .del();
+
+ // Respond to the request.
+ res.status(200).send();
+
+ } catch (e) {
+ catchUnhandledErrors(e);
+ trx.rollback();
+ }
+ })
+}
\ No newline at end of file
diff --git a/server/endpoints/MergeTagEndpointHandler.ts b/server/endpoints/MergeTagEndpointHandler.ts
new file mode 100644
index 0000000..b84db4b
--- /dev/null
+++ b/server/endpoints/MergeTagEndpointHandler.ts
@@ -0,0 +1,73 @@
+import * as api from '../../client/src/api';
+import { EndpointError, EndpointHandler, catchUnhandledErrors } from './types';
+import Knex from 'knex';
+
+export const MergeTagEndpointHandler: EndpointHandler = async (req: any, res: any, knex: Knex) => {
+ if (!api.checkMergeTagRequest(req)) {
+ const e: EndpointError = {
+ internalMessage: 'Invalid MergeTag request: ' + JSON.stringify(req.body),
+ httpStatus: 400
+ };
+ throw e;
+ }
+ const reqObject: api.DeleteTagRequest = req.body;
+
+ console.log("Merge Tag:", reqObject);
+ const fromId = req.params.id;
+ const toId = req.params.toId;
+
+ await knex.transaction(async (trx) => {
+ try {
+ // Start retrieving the "from" tag.
+ const fromTagPromise = trx.select('id')
+ .from('tags')
+ .where({ id: fromId })
+ .then((r: any) => (r && r[0]) ? r[0]['id'] : undefined)
+
+ // Start retrieving the "to" tag.
+ const toTagPromise = trx.select('id')
+ .from('tags')
+ .where({ id: toId })
+ .then((r: any) => (r && r[0]) ? r[0]['id'] : undefined)
+
+ // Wait for the requests to finish.
+ var [fromTag, toTag] = await Promise.all([fromTagPromise, toTagPromise]);
+
+ // Check that we found all objects we need.
+ if (!fromTag || !toTag) {
+ const e: EndpointError = {
+ internalMessage: 'Source or target tag does not exist for MergeTag request: ' + JSON.stringify(req.body),
+ httpStatus: 400
+ };
+ throw e;
+ }
+
+ // Assign new tag ID to any objects referencing the to-be-merged tag.
+ const cPromise = trx('tags')
+ .where({ 'parentId': fromId })
+ .update({ 'parentId': toId });
+ const sPromise = trx('songs_tags')
+ .where({ 'tagId': fromId })
+ .update({ 'tagId': toId });
+ const arPromise = trx('artists_tags')
+ .where({ 'tagId': fromId })
+ .update({ 'tagId': toId });
+ const alPromise = trx('albums_tags')
+ .where({ 'tagId': fromId })
+ .update({ 'tagId': toId });
+ await Promise.all([sPromise, arPromise, alPromise, cPromise]);
+
+ // Delete the original tag.
+ await trx('tags')
+ .where({ 'id': fromId })
+ .del();
+
+ // Respond to the request.
+ res.status(200).send();
+
+ } catch (e) {
+ catchUnhandledErrors(e);
+ trx.rollback();
+ }
+ })
+}
\ No newline at end of file
diff --git a/server/endpoints/QueryEndpointHandler.ts b/server/endpoints/QueryEndpointHandler.ts
index 00ad1d5..89fb35d 100644
--- a/server/endpoints/QueryEndpointHandler.ts
+++ b/server/endpoints/QueryEndpointHandler.ts
@@ -179,7 +179,7 @@ const objectColumns = {
};
function constructQuery(knex: Knex, queryFor: ObjectType, queryElem: api.QueryElem, ordering: api.Ordering,
- offset: number, limit: number) {
+ offset: number, limit: number | null) {
const joinObjects = getRequiredDatabaseObjects(queryElem);
joinObjects.delete(queryFor); // We are already querying this object in the base query.
@@ -213,7 +213,12 @@ function constructQuery(knex: Knex, queryFor: ObjectType, queryElem: api.QueryEl
(ordering.ascending ? 'asc' : 'desc'));
// Apply limiting.
- q = q.limit(limit).offset(offset);
+ if(limit !== null) {
+ q = q.limit(limit)
+ }
+
+ // Apply offsetting.
+ q = q.offset(offset);
return q;
}
@@ -271,43 +276,43 @@ export const QueryEndpointHandler: EndpointHandler = async (req: any, res: any,
const albumLimit = reqObject.offsetsLimits.albumLimit;
const albumOffset = reqObject.offsetsLimits.albumOffset;
- const artistsPromise: Promise = (artistLimit && artistLimit > 0) ?
+ const artistsPromise: Promise = (artistLimit && artistLimit !== 0) ?
constructQuery(knex,
ObjectType.Artist,
reqObject.query,
reqObject.ordering,
artistOffset || 0,
- artistLimit
+ artistLimit >= 0 ? artistLimit : null,
) :
(async () => [])();
- const albumsPromise: Promise = (albumLimit && albumLimit > 0) ?
+ const albumsPromise: Promise = (albumLimit && albumLimit !== 0) ?
constructQuery(knex,
ObjectType.Album,
reqObject.query,
reqObject.ordering,
artistOffset || 0,
- albumLimit
+ albumLimit >= 0 ? albumLimit : null,
) :
(async () => [])();
- const songsPromise: Promise = (songLimit && songLimit > 0) ?
+ const songsPromise: Promise = (songLimit && songLimit !== 0) ?
constructQuery(knex,
ObjectType.Song,
reqObject.query,
reqObject.ordering,
songOffset || 0,
- songLimit
+ songLimit >= 0 ? songLimit : null,
) :
(async () => [])();
- const tagsPromise: Promise = (tagLimit && tagLimit > 0) ?
+ const tagsPromise: Promise = (tagLimit && tagLimit !== 0) ?
constructQuery(knex,
ObjectType.Tag,
reqObject.query,
reqObject.ordering,
tagOffset || 0,
- tagLimit
+ tagLimit >= 0 ? tagLimit : null,
) :
(async () => [])();
@@ -318,12 +323,12 @@ export const QueryEndpointHandler: EndpointHandler = async (req: any, res: any,
const ids = songs.map((song: any) => song['songs.id']);
return ids;
})();
- const songsArtistsPromise: Promise> = (songLimit && songLimit > 0) ?
+ const songsArtistsPromise: Promise> = (songLimit && songLimit !== 0) ?
(async () => {
return await getLinkedObjects(knex, ObjectType.Song, ObjectType.Artist, await songIdsPromise);
})() :
(async () => { return {}; })();
- const songsTagsPromise: Promise> = (songLimit && songLimit > 0) ?
+ const songsTagsPromise: Promise> = (songLimit && songLimit !== 0) ?
(async () => {
const tagsPerSong: Record = await getLinkedObjects(knex, ObjectType.Song, ObjectType.Tag, await songIdsPromise);
var result: Record = {};
@@ -338,7 +343,7 @@ export const QueryEndpointHandler: EndpointHandler = async (req: any, res: any,
return result;
})() :
(async () => { return {}; })();
- const songsAlbumsPromise: Promise> = (songLimit && songLimit > 0) ?
+ const songsAlbumsPromise: Promise> = (songLimit && songLimit !== 0) ?
(async () => {
return await getLinkedObjects(knex, ObjectType.Song, ObjectType.Album, await songIdsPromise);
})() :