diff --git a/package-lock.json b/package-lock.json index fa756c0..4bce804 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,12 @@ "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", + "dev": true + }, "@types/classnames": { "version": "2.2.9", "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz", @@ -128,6 +134,31 @@ "integrity": "sha1-a9p9uGU/piZD9e5p6facEaOS46Y=", "dev": true }, + "@types/request": { + "version": "2.48.3", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.3.tgz", + "integrity": "sha512-3Wo2jNYwqgXcIz/rrq18AdOZUQB8cQ34CXZo+LUwPJNpvRAL86+Kc2wwI8mqpz9Cr1V+enIox5v+WZhy/p3h8w==", + "dev": true, + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -140,6 +171,12 @@ "integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==", "dev": true }, + "@types/tough-cookie": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz", + "integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==", + "dev": true + }, "@types/uglify-js": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.4.tgz", @@ -557,7 +594,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -603,8 +639,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assign-symbols": { "version": "1.0.0", @@ -627,8 +662,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.2", @@ -664,14 +698,12 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "balanced-match": { "version": "1.0.0", @@ -743,7 +775,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, "requires": { "tweetnacl": "^0.14.3" } @@ -1014,8 +1045,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "2.4.2", @@ -1195,7 +1225,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -1311,8 +1340,7 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "create-ecdh": { "version": "4.0.3", @@ -1490,7 +1518,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -1585,8 +1612,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", @@ -1695,6 +1721,11 @@ "domelementtype": "1" } }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -1743,7 +1774,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -2020,8 +2050,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -2112,8 +2141,7 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-decode-uri-component": { "version": "1.0.1", @@ -2210,6 +2238,54 @@ "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", "dev": true }, + "file-loader": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.2.0.tgz", + "integrity": "sha512-+xZnaK5R8kBJrHK0/6HRlrKNamvVS5rjyuju+rnyxRGuwUJwpAMsVzUl5dz6rK8brkzjV6JpcFNjp6NqV0g1OQ==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.0.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "schema-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", + "integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2331,14 +2407,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -3099,7 +3173,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -3193,14 +3266,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -3400,7 +3471,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -3704,8 +3774,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", @@ -3746,8 +3815,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "jmespath": { "version": "0.15.0", @@ -3769,8 +3837,7 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "json-parse-better-errors": { "version": "1.0.2", @@ -3781,8 +3848,7 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.4.1", @@ -3792,8 +3858,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "0.5.1", @@ -3805,7 +3870,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -4047,6 +4111,12 @@ } } }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", @@ -4113,14 +4183,12 @@ "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "dev": true + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" }, "mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dev": true, "requires": { "mime-db": "1.40.0" } @@ -4540,6 +4608,79 @@ "sort-keys": "^1.0.0" } }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -4579,8 +4720,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", @@ -4923,8 +5063,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { "version": "2.0.7", @@ -4932,6 +5071,12 @@ "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", "dev": true }, + "pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -5140,8 +5285,7 @@ "psl": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", - "dev": true + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" }, "public-encrypt": { "version": "4.0.3", @@ -5198,8 +5342,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "4.3.4", @@ -5443,7 +5586,6 @@ "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -5608,8 +5750,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass-graph": { "version": "2.2.4", @@ -5964,6 +6105,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -6195,7 +6342,6 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -6421,6 +6567,17 @@ } } }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, "string.prototype.trimleft": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", @@ -6706,7 +6863,6 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -6715,8 +6871,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" } } }, @@ -6854,7 +7009,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -6862,8 +7016,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "typedarray": { "version": "0.0.6", @@ -7062,8 +7215,7 @@ "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", - "dev": true + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, "v8-compile-cache": { "version": "2.0.3", @@ -7085,7 +7237,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index e130977..1041237 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,10 @@ "private": true, "scripts": { "start": "node dist/server/index.js", - "build": "tsc", - "build:text-app": "webpack --config src/apps/text-app/composer/webpack.config.ts" + "build:server": "tsc", + "build:text-app": "webpack --config src/apps/text-app/composer/webpack.config.ts", + "build:gif-app": "webpack --config src/apps/gif-app/composer/webpack.config.ts", + "build": "run-s build:server build:text-app build:gif-app" }, "devDependencies": { "@types/classnames": "^2.2.9", @@ -14,12 +16,15 @@ "@types/mini-css-extract-plugin": "^0.8.0", "@types/react": "^16.9.9", "@types/react-dom": "^16.9.2", + "@types/request": "^2.48.3", "@types/webpack": "^4.39.5", "bulma": "^0.8.0", "css-loader": "^3.2.0", + "file-loader": "^4.2.0", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.8.0", "node-sass": "^4.12.0", + "npm-run-all": "^4.1.5", "pino-pretty": "^3.2.2", "sass-loader": "^8.0.0", "style-loader": "^1.0.0", @@ -34,9 +39,11 @@ "@fortawesome/free-solid-svg-icons": "^5.11.2", "@fortawesome/react-fontawesome": "^0.1.7", "classnames": "^2.2.6", + "dotenv": "^8.2.0", "fastify": "^2.10.0", "fastify-static": "^2.5.0", "react": "^16.10.2", - "react-dom": "^16.10.2" + "react-dom": "^16.10.2", + "request": "^2.88.0" } } diff --git a/src/apps/gif-app/composer/app.tsx b/src/apps/gif-app/composer/app.tsx new file mode 100644 index 0000000..d6cdf0e --- /dev/null +++ b/src/apps/gif-app/composer/app.tsx @@ -0,0 +1,163 @@ +import React, { FC, useState, useEffect } from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faSearch } from '@fortawesome/free-solid-svg-icons' +import { Communicator } from '../../../communicator' +import classNames from 'classnames' +import Gif from './gif' +import { ClassDictionary, GiphyGif } from '../../../types' + +const giphyIcon = require('./giphy.png') +import '../../../styles/default.scss' + +type APIResponse = GiphyGif[] + +interface Props { + communicator: Communicator +} + +const useDebounce = (value: string, delay = 500) => { + const [valueD, setValueD] = useState(value) + + useEffect(() => { + const handler = setTimeout(() => { + setValueD(value) + }, delay) + + return () => { + clearTimeout(handler) + } + }, [value, delay]) + + return valueD +} + +const App: FC = ({ communicator }) => { + const [posting, setPosting] = useState(false) + const [searching, setSearching] = useState(false) + const [search, setSearch] = useState('') + const searchD = useDebounce(search) + const [gifs, setGifs] = useState([]) + const [selected, setSelected] = useState('') + + const buttonStyle: ClassDictionary = { + 'button': true, + 'is-primary': true, + 'is-loading': posting, + } + + const controlStyle: ClassDictionary = { + 'control': true, + 'has-icons-left': true, + 'is-loading': searching, + } + + useEffect(() => { + const init = async () => { + try { + const content = await communicator.init() + if (content && content.parent && content.parent.data && content.parent.data.search) { + setSearch(content.parent.data.search) + } + + await communicator.setHeight(1000) + } catch (err) { + console.error(err) + } + } + + init() + }, []) + + useEffect(() => { + const doTrending = async () => { + try { + const response = await fetch('/api/gifs/home') + setGifs(await response.json() as APIResponse) + } catch (err) { + console.error(err) + } + } + + const doSearch = async () => { + try { + setSearching(true) + const response = await fetch(`/api/gifs/search?q=${search}`) + setGifs(await response.json() as APIResponse) + } catch (err) { + console.error(err) + } + + setSearching(false) + } + + if (!search) { + doTrending() + } else if (search.length > 2) { + doSearch() + } + }, [searchD]) + + const handleSearchChange = (search: string) => { + setSearch(search) + } + + const post = async () => { + try { + const gif = gifs.find(g => g.id === selected) + if (!gif) return + + setPosting(true) + + await communicator.post({ + attachments: [{ + url: gif.images.fixed_height.url, + text: gif.title, + }], + data: { + search, + }, + visible: true, + }) + + setSearch('') + setSelected('') + } catch (err) { + console.error(err) + } + + setPosting(false) + } + + return ( +
+
+

+ handleSearchChange(e.target.value)} /> + + + +

+
+ +
+ {gifs.map(gif => )} +
+ + +
+ ) +} + +export default App diff --git a/src/apps/gif-app/composer/gif.tsx b/src/apps/gif-app/composer/gif.tsx new file mode 100644 index 0000000..b01c229 --- /dev/null +++ b/src/apps/gif-app/composer/gif.tsx @@ -0,0 +1,24 @@ +import React, { FC, useState, useEffect } from 'react' +import classNames from 'classnames' +import { ClassDictionary, GiphyGif } from '../../../types' + +interface Props { + gif: GiphyGif + selected: boolean + onSelect: (id: string) => void +} + +const Gif: FC = ({ gif, selected, onSelect }) => { + const classes: ClassDictionary = { + gif: true, + selected, + } + + return ( +
onSelect(gif.id)}> + {gif.title} +
+ ) +} + +export default Gif diff --git a/src/apps/gif-app/composer/giphy.png b/src/apps/gif-app/composer/giphy.png new file mode 100644 index 0000000..7121d2d Binary files /dev/null and b/src/apps/gif-app/composer/giphy.png differ diff --git a/src/apps/gif-app/composer/index.ejs b/src/apps/gif-app/composer/index.ejs new file mode 100644 index 0000000..2ee9a43 --- /dev/null +++ b/src/apps/gif-app/composer/index.ejs @@ -0,0 +1,12 @@ + + + + + GIF App + + + + +
+ + diff --git a/src/apps/gif-app/composer/index.tsx b/src/apps/gif-app/composer/index.tsx new file mode 100644 index 0000000..227de9d --- /dev/null +++ b/src/apps/gif-app/composer/index.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import { render } from 'react-dom' +import App from './app' +import { Communicator } from '../../../communicator' + +render(, document.getElementById('app')) diff --git a/src/apps/gif-app/composer/webpack.config.ts b/src/apps/gif-app/composer/webpack.config.ts new file mode 100644 index 0000000..b7ee9c2 --- /dev/null +++ b/src/apps/gif-app/composer/webpack.config.ts @@ -0,0 +1,63 @@ +import { resolve } from 'path' +import { Configuration } from 'webpack' +import HtmlWebpackPlugin from 'html-webpack-plugin' +import MiniCssExtractPlugin from 'mini-css-extract-plugin' + +const config: Configuration = { + mode: 'development', + devtool: 'eval-source-map', + entry: { + app: resolve(__dirname, './index.tsx'), + }, + output: { + path: resolve(__dirname, '../../../../dist/apps/gif-app/'), + filename: '[name].js', + }, + optimization: { + splitChunks: { + chunks: 'all', + }, + }, + resolve: { + extensions: ['.ts', '.tsx', '.js', '.png'], + }, + module: { + rules: [ + { + test: /\.ts(x?)$/, + exclude: /node_modules/, + use: 'ts-loader', + }, + { + test: /\.scss$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + { + loader: 'sass-loader', + options: { + sourceMap: true, + }, + }, + ], + }, + { + test: /\.(jpe?g|gif|png|svg)$/, + use: ['file-loader'], + }, + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + title: 'GIF App', + hash: true, + template: resolve(__dirname, './index.ejs'), + filename: 'composer.html', + }), + new MiniCssExtractPlugin({ + filename: '[name].css', + }), + ], +} + +export default config diff --git a/src/apps/text-app/composer/app.tsx b/src/apps/text-app/composer/app.tsx index f2f9e3f..ca11ab4 100644 --- a/src/apps/text-app/composer/app.tsx +++ b/src/apps/text-app/composer/app.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faEyeSlash } from '@fortawesome/free-solid-svg-icons' import { Communicator } from '../../../communicator' import classNames from 'classnames' -import { ClassDictionary } from '../../../types' +import { ClassDictionary, Post } from '../../../types' import '../../../styles/default.scss' @@ -16,6 +16,7 @@ const App: FC = ({ communicator }) => { const [content, setContent] = useState('') const [cover, setCover] = useState('') const [posting, setPosting] = useState(false) + const [parent, setParent] = useState(null) const charactersLeft = maxCharacters - content.length const showCharactersLeft = charactersLeft < (maxCharacters / 2) @@ -31,10 +32,14 @@ const App: FC = ({ communicator }) => { 'is-loading': posting, } + const buttonText = parent ? 'Reply!' : 'Post!' + useEffect(() => { const init = async () => { try { - await communicator.init() + const response = await communicator.init() + if (response!.parent) setParent(response!.parent) + await communicator.setHeight(document.body.offsetHeight) } catch (err) { console.error('App Component: ', err) @@ -49,7 +54,11 @@ const App: FC = ({ communicator }) => { try { setPosting(true) - await communicator.post(true, content, cover) + await communicator.post({ + text: content, + cover, + visible: true, + }) setContent('') setCover('') } catch (err) { @@ -85,7 +94,7 @@ const App: FC = ({ communicator }) => {
- +
diff --git a/src/apps/text-app/composer/index.ejs b/src/apps/text-app/composer/index.ejs index 31e1aa8..d229017 100644 --- a/src/apps/text-app/composer/index.ejs +++ b/src/apps/text-app/composer/index.ejs @@ -2,7 +2,7 @@ - Flexor + Text App diff --git a/src/apps/text-app/composer/index.tsx b/src/apps/text-app/composer/index.tsx index ebec225..5a06252 100644 --- a/src/apps/text-app/composer/index.tsx +++ b/src/apps/text-app/composer/index.tsx @@ -3,6 +3,4 @@ import { render } from 'react-dom' import App from './app' import { Communicator } from '../../../communicator' -const communicator = new Communicator('1c28b52e4b1c93c0cc78') - -render(, document.getElementById('app')) +render(, document.getElementById('app')) diff --git a/src/apps/text-app/composer/webpack.config.ts b/src/apps/text-app/composer/webpack.config.ts index ca458e4..174150b 100644 --- a/src/apps/text-app/composer/webpack.config.ts +++ b/src/apps/text-app/composer/webpack.config.ts @@ -11,7 +11,6 @@ const config: Configuration = { }, output: { path: resolve(__dirname, '../../../../dist/apps/text-app/'), - // publicPath: '/', filename: '[name].js', }, optimization: { diff --git a/src/communicator/index.ts b/src/communicator/index.ts index 0061f91..86282b5 100644 --- a/src/communicator/index.ts +++ b/src/communicator/index.ts @@ -1,6 +1,9 @@ +import { Post } from '../types' + export interface MessageContent { [key: string]: any height?: number + parent?: Post } export interface IncomingMessageData { @@ -26,9 +29,7 @@ interface ListenerCollection { [name: string]: Listener } -interface AppSettings { - [key: string]: any -} +type PostOptions = Post export class Communicator { private origin = 'http://localhost:8080' @@ -88,12 +89,7 @@ export class Communicator { return this.postAndReceive('setHeight', { height }) } - async post(visible: boolean, text?: string, cover?: string, data?: object) { - return this.postAndReceive('post', { - visible, - text, - cover, - data, - }) + async post(options: PostOptions) { + return this.postAndReceive('post', options) } } diff --git a/src/server/api/index.ts b/src/server/api/index.ts new file mode 100644 index 0000000..c2f7a5a --- /dev/null +++ b/src/server/api/index.ts @@ -0,0 +1,145 @@ +import { + FastifyInstance, + RouteShorthandOptions, + Plugin, + DefaultQuery, + DefaultParams, + DefaultHeaders, + DefaultBody, + JSONSchema, +} from 'fastify' +import { Server, IncomingMessage, ServerResponse } from 'http' +import request from 'request' +import { PluginOptions, GiphyResponse } from '../../types' + +interface FetchOptions { + url: string + params?: string[][] + method?: string +} + +const paramsToString = (params: string[][]) => params.map(p => p.join('=')).join('&') + +const giphyGifSchema: JSONSchema = { + type: 'object', + properties: { + type: { type: 'string' }, + id: { type: 'string' }, + slug: { type: 'string' }, + url: { type: 'string' }, + bitly_url: { type: 'string' }, + embed_url: { type: 'string' }, + title: { type: 'string' }, + images: { + type: 'object', + properties: { + fixed_height: { + type: 'object', + properties: { + url: { type: 'string' }, + }, + }, + fixed_height_still: { + type: 'object', + properties: { + url: { type: 'string' }, + }, + }, + fixed_height_downsampled: { + type: 'object', + properties: { + url: { type: 'string' }, + }, + }, + }, + }, + }, +} + +async function fetch(options: FetchOptions) { + const { url, params = [], method = 'get' } = options + const uri = params.length > 0 ? `${url}?${paramsToString(params)}` : url + + return new Promise((resolve, reject) => { + request({ + uri, + method, + json: true, + }, function(error, response, body) { + if (error) { + reject(error) + return + } + + resolve(body) + }) + }) +} + +function gifHomeRoute(server: FastifyInstance) { + const options: RouteShorthandOptions = { + schema: { + response: { + 200: { + type: 'array', + items: giphyGifSchema, + }, + }, + }, + } + + server.get('/api/gifs/home', options, async (request, reply) => { + const response = await fetch({ + url: `${process.env.GIPHY_API_ENDPOINT!}/v1/gifs/trending`, + params: [['api_key', process.env.GIPHY_API_KEY!], ['limit', '20']], + }) + + return response.data + }) +} + +function gifSearchRoute(server: FastifyInstance) { + interface Query { + q: string + } + + const options: RouteShorthandOptions = { + schema: { + querystring: { + type: 'object', + properties: { + q: { type: 'string' }, + }, + }, + response: { + 200: { + type: 'array', + items: giphyGifSchema, + }, + }, + }, + } + + server.get('/api/gifs/search', options, async (request, reply) => { + const q = request.query.q ? request.query.q.trim() : '' + + if (q.length < 3) { + reply.code(400) + return + } + + const response = await fetch({ + url: `${process.env.GIPHY_API_ENDPOINT!}/v1/gifs/search`, + params: [['api_key', process.env.GIPHY_API_KEY!], ['limit', '20'], ['q', q]], + }) + + return response.data + }) +} + +const plugin: Plugin = async server => { + gifHomeRoute(server) + gifSearchRoute(server) +} + +export default plugin diff --git a/src/server/index.ts b/src/server/index.ts index b0b28c9..2e2c7cc 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,22 +1,28 @@ +import { config } from 'dotenv' import { resolve } from 'path' import fastify from 'fastify' import fastifyStatic from 'fastify-static' -const port = 8082 +import api from './api' + +config() + const server = fastify({ logger: { - level: 'info', - prettyPrint: true, + level: process.env.LOGGER_LEVEL, + prettyPrint: process.env.LOGGER_PRETTY_PRINT === 'true', } }) +server.register(api) + server.register(fastifyStatic, { root: resolve(__dirname, '..', 'apps'), }) const start = async () => { try { - await server.listen(port) + await server.listen(process.env.PORT!) } catch (err) { process.exit(1) } diff --git a/src/styles/default.scss b/src/styles/default.scss index de08b40..e6c6ba5 100644 --- a/src/styles/default.scss +++ b/src/styles/default.scss @@ -31,3 +31,20 @@ $body-size: 14px; body { padding: 10px; } + +div.gifs { + display: flex; + flex-wrap: wrap; +} + +div.gif { + margin: 5px; + + img { + height: 100px; + } +} + +.gif.selected { + border: solid 3px $green; +} diff --git a/src/types/index.ts b/src/types/index.ts index c87b774..eed9081 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,61 @@ export interface ClassDictionary { [name: string]: boolean } + +export interface PluginOptions { + +} + +export interface Attachment { + url: string + text?: string + cover?: string +} + +export interface PostData { + [key: string]: any +} + +export interface Post { + text?: string + cover?: string + attachments?: Attachment[] + data?: PostData + visible: boolean +} + +export interface GiphyGif { + type: string + id: string + slug: string + url: string + bitly_url: string + embed_url: string + username: string + source: string + rating: string + content_url: string + title: string + images: { + fixed_height: { + url: string + } + fixed_height_still: { + url: string + } + fixed_height_downsampled: { + url: string + } + } +} + +export interface GiphyPagination { + offset: number + total_count: number + count: number +} + +export interface GiphyResponse { + data: GiphyGif[] + pagination: GiphyPagination +} diff --git a/tsconfig.json b/tsconfig.json index 85682c0..15ea417 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "outDir": "./dist/server/", + "outDir": "./dist/", "baseUrl": ".", "sourceMap": true, "strict": true,