diff --git a/.devcontainer.json b/.devcontainer.json index 7de21a4..81782e8 100644 --- a/.devcontainer.json +++ b/.devcontainer.json @@ -1,6 +1,8 @@ { "name": "TS-ESWeb Container", - "dockerFile": "./Dockerfile.dev", + "dockerComposeFile": "./docker-compose.dev.yml", + "service": "TSESWeb", + "runServices": ["TSESWeb", "Redis", "Loki", "Grafana"], "settings": { // This dev container does include /bin/bash if you prefer to use it instead of ash. "terminal.integrated.shell.linux": "/bin/bash" @@ -18,10 +20,6 @@ "redhat.vscode-yaml" ], "remoteUser": "node", - "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached", "workspaceFolder": "/workspace", - "mounts": [ - "source=ts-esweb-modules,target=/workspace/node_modules,type=volume" - ], "postCreateCommand": "sudo chown node node_modules && npm i" } diff --git a/.gitignore b/.gitignore index 602dcb4..46acda9 100644 --- a/.gitignore +++ b/.gitignore @@ -139,4 +139,5 @@ temp/ # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) -dist \ No newline at end of file +dist +Archive \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 105d3ef..99cb82e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,10 +8,18 @@ "protocol": "inspector", "port": 9229, "restart": true, + "showAsyncStacks": true, + "autoAttachChildProcesses": true, + "console": "integratedTerminal", + "outputCapture": "std", + "sourceMaps": true, "cwd": "${workspaceFolder}", - "runtimeExecutable": "npx", - "runtimeArgs": ["nodemon"] + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "start:debug"], + "env": { + "LOG_LEVEL": "SILLY" + } } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 3662b37..25fa621 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file +} diff --git a/Extras/Loki/config.yaml b/Extras/Loki/config.yaml new file mode 100644 index 0000000..c03f972 --- /dev/null +++ b/Extras/Loki/config.yaml @@ -0,0 +1,69 @@ +auth_enabled: false + + + +server: + http_listen_port: 3100 + grpc_server_max_recv_msg_size: 104857600 + grpc_server_max_send_msg_size: 104857600 + grpc_server_max_concurrent_streams: 1000 + +ingester: + lifecycler: + address: 127.0.0.1 + ring: + kvstore: + store: inmemory + replication_factor: 1 + final_sleep: 0s + chunk_idle_period: 1h # Any chunk not receiving new logs in this time will be flushed + max_chunk_age: 1h # All chunks will be flushed when they hit this age, default is 1h + chunk_target_size: 1048576 # Loki will attempt to build chunks up to 1.5MB, flushing first if chunk_idle_period or max_chunk_age is reached first + chunk_retain_period: 30s # Must be greater than index read cache TTL if using an index cache (Default index read cache TTL is 5m) + max_transfer_retries: 0 # Chunk transfers disabled + +schema_config: + configs: + - from: 2020-10-24 + store: boltdb-shipper + object_store: filesystem + schema: v11 + index: + prefix: index_ + period: 24h + +storage_config: + boltdb_shipper: + active_index_directory: /loki/boltdb-shipper-active + cache_location: /loki/boltdb-shipper-cache + cache_ttl: 24h # Can be increased for faster performance over longer query periods, uses more disk space + shared_store: filesystem + filesystem: + directory: /loki/chunks + +compactor: + working_directory: /loki/boltdb-shipper-compactor + shared_store: filesystem + +limits_config: + reject_old_samples: true + reject_old_samples_max_age: 168h + +chunk_store_config: + max_look_back_period: 0s + +table_manager: + retention_deletes_enabled: false + retention_period: 0s + +ruler: + storage: + type: local + local: + directory: /loki/rules + rule_path: /loki/rules-temp + alertmanager_url: http://localhost:9093 + ring: + kvstore: + store: inmemory + enable_api: true \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..b50d74a --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,44 @@ +version: '3.8' + +volumes: + modules: + +networks: + internalRedis: + internalLoki: + +services: + TSESWeb: + image: ts-esweb:dev + command: /bin/sh -c "while sleep 1000; do :; done" + user: node + build: + context: ./ + dockerfile: Dockerfile.dev + volumes: + # API Folders + - ./:/workspace:cached + - modules:/workspace/node_modules + networks: + - internalRedis + - internalLoki + + Redis: + image: redis:alpine3.12 + networks: + - internalRedis + + Loki: + image: grafana/loki:latest + command: -config.file=/etc/loki/config.yaml + volumes: + - ./Extras/Loki/config.yaml:/etc/loki/config.yaml:ro + networks: + - internalLoki + + Grafana: + image: grafana/grafana:latest + ports: + - 3000:3000 + networks: + - internalLoki \ No newline at end of file diff --git a/nodemon.json b/nodemon.json index 452c43f..3446adb 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,6 +1,9 @@ { + "$schema": "https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/nodemon.json", "watch": ["src"], "ext": "ts,tsx", "ignore": ["src/**/*.test.ts"], - "exec": "npm run start:debug" + "exec": "npm run start:debug", + "spawn": true, + "stdin": false } diff --git a/package-lock.json b/package-lock.json index b119566..387b6ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,19 +11,26 @@ "dependencies": { "@k-foss/ts-worker": "^1.0.5", "apollo-server-fastify": "^3.0.0-alpha.3", + "bullmq": "^1.14.4", "cjstoesm": "^0.1.3", "fastify": "^3.11.0", "fastify-websocket": "^3.0.0", "get-port": "^5.1.1", "graphql": "^15.4.0", + "hyperid": "^2.1.0", + "ioredis": "^4.22.0", + "p-event": "^4.2.0", + "react-refresh-typescript": "^1.1.1", "reflect-metadata": "^0.1.13", "type-graphql": "^1.1.1", - "winston": "^3.3.3" + "winston": "^3.3.3", + "winston-loki": "^6.0.0-rc.9" }, "devDependencies": { "@k-foss/ts-esnode": "^2.0.0", "@k-foss/ts-estests": "^1.0.6", - "@types/node": "^14.14.25", + "@material-ui/core": "^5.0.0-alpha.25", + "@types/node": "^14.14.31", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@types/workbox-sw": "^4.3.1", @@ -44,8 +51,9 @@ "prettier": "^2.2.1", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-router-dom": "^6.0.0-beta.0", "typedi": "^0.10.0", - "typescript": "^4.1.3" + "typescript": "^4.2.2" } }, "node_modules/@apollo/protobufjs": { @@ -138,6 +146,15 @@ "node": ">=4" } }, + "node_modules/@babel/runtime": { + "version": "7.13.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", + "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -148,6 +165,49 @@ "kuler": "^2.0.0" } }, + "node_modules/@emotion/cache": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.1.3.tgz", + "integrity": "sha512-n4OWinUPJVaP6fXxWZD9OUeQ0lY7DvtmtSuqtRWT0Ofo/sBLCVSgb4/Oa0Q5eFxcwablRKjUXqXtNZVyEwCAuA==", + "dev": true, + "dependencies": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.0.0", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "^4.0.3" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true + }, + "node_modules/@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==", + "dev": true + }, + "node_modules/@emotion/sheet": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.1.tgz", + "integrity": "sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g==", + "dev": true + }, + "node_modules/@emotion/utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", + "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==", + "dev": true + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", + "dev": true + }, "node_modules/@eslint/eslintrc": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", @@ -212,6 +272,213 @@ "node": ">= 13.9" } }, + "node_modules/@material-ui/core": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-5.0.0-alpha.25.tgz", + "integrity": "sha512-TtU3m3qw0FKtrDHCPmakJ25/20bGPRkLn8qjDkyCUYfg7kG4RLIwgiI0fKnF5PCDu46qL+s233+nVwAh8Iq+qw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/styled-engine": "5.0.0-alpha.25", + "@material-ui/styles": "5.0.0-alpha.25", + "@material-ui/system": "5.0.0-alpha.25", + "@material-ui/types": "5.1.7", + "@material-ui/unstyled": "5.0.0-alpha.25", + "@material-ui/utils": "5.0.0-alpha.25", + "@popperjs/core": "^2.4.4", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "csstype": "^3.0.2", + "hoist-non-react-statics": "^3.3.2", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0", + "@emotion/styled": "^11.0.0", + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/styled-engine": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/styled-engine/-/styled-engine-5.0.0-alpha.25.tgz", + "integrity": "sha512-z9piABOWycFx1yA9Th8MAs/03SfVOobh+4X1owZmOzXVSln3Xm0C3GjsFmfUpYMC8oC0vddFj00IcCCxm0OECg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@emotion/cache": "^11.0.0", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0", + "@emotion/styled": "^11.0.0", + "react": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@material-ui/styles": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-5.0.0-alpha.25.tgz", + "integrity": "sha512-VODyi/JT0njmkcyRKiYPacKQmhS11bidg6AMTgAU5KjdmchFPRIl5lwIHFYeRtVEsHX9/9tE0pPlCJIhVYAYsw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.7", + "@material-ui/utils": "5.0.0-alpha.25", + "clsx": "^1.0.4", + "csstype": "^3.0.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/system": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-5.0.0-alpha.25.tgz", + "integrity": "sha512-4TrsBfB9MMsUj8o1jGRBI4Zv1SSASDwg1lfad5SYtlGhL+0LiSDO/XhVvOvYaRi749lguux2rlrVZpIut0jgMA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "5.0.0-alpha.25", + "csstype": "^3.0.2", + "prop-types": "^15.7.2" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/material-ui" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/types": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.7.tgz", + "integrity": "sha512-OSpB0gEKZm5h4izTLyipb34PkfazpvusgQMDTmFkSuqcKoChTshfGejEYX6uaZ+4m5xlT5qzihE6eKA+JnjELg==", + "dev": true, + "peerDependencies": { + "@types/react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/unstyled": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/unstyled/-/unstyled-5.0.0-alpha.25.tgz", + "integrity": "sha512-LqAXDhSk2GC3C3SX0U5V3U0WA6gAS5Ayrozo9Br2XtxfEDdPTDmR1R7csT+DruyP3vGBs4k4ILIUz8KfC/kjww==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "5.0.0-alpha.25", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@material-ui/utils": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-5.0.0-alpha.25.tgz", + "integrity": "sha512-wgW1ua+ncAU6lrE2bFhd9iU1xmpxj1KL+YRJa0PoFpFgLNVpSTCVHynMlRAERNP+CteazFHj5upkigqJpV406Q==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.4.4", + "@types/prop-types": "^15.7.3", + "@types/react-is": "^16.7.1 || ^17.0.0", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -247,6 +514,16 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.8.4.tgz", + "integrity": "sha512-h0lY7g36rhjNV8KVHKS3/BEOgfsxu0AiRI8+ry5IFBGEsQFkpjxtcpVc9ndN8zrKUeMZXAWMc7eQMepfgykpxQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -337,6 +614,14 @@ "@types/node": "*" } }, + "node_modules/@types/ioredis": { + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.19.3.tgz", + "integrity": "sha512-r9rXLYeZVmvTf0nKDmFYrpBeVzK5B6jgE2ncfkzPOAuRM5RTzKnYdW2wVn+ZONMnFxV6H4R3NrCdRdC3CL4nFA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", @@ -360,9 +645,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "node_modules/@types/node": { - "version": "14.14.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz", - "integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==" + "version": "14.14.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz", + "integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==" }, "node_modules/@types/node-fetch": { "version": "2.5.7", @@ -398,6 +683,24 @@ "@types/react": "*" } }, + "node_modules/@types/react-is": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.0.tgz", + "integrity": "sha512-A0DQ1YWZ0RG2+PV7neAotNCIh8gZ3z7tQnDJyS2xRPDNtAtSPcJ9YyfMP8be36Ha0kQRzbZCrrTMznA4blqO5g==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", @@ -1135,11 +1438,57 @@ "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" } }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, "node_modules/archy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" }, + "node_modules/are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1243,6 +1592,14 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/async-retry": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", @@ -1289,6 +1646,55 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "optional": true, + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/bl/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -1394,6 +1800,54 @@ "node": ">=8" } }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "optional": true, + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "optional": true + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "optional": true + }, + "node_modules/bullmq": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-1.14.4.tgz", + "integrity": "sha512-Gx5NX+wA+5E1IReSWuW/ybeq0+JUKUk5BvkiO6QWKowrnJaGJLi8r1rl/VSE7h4reErslLmcXqEvBrpg0yMOwg==", + "dependencies": { + "@types/ioredis": "^4.16.3", + "cron-parser": "^2.7.3", + "get-port": "^5.0.0", + "ioredis": "^4.17.3", + "lodash": "^4.17.11", + "semver": "^6.3.0", + "tslib": "^1.10.0", + "uuid": "^8.2.0" + } + }, "node_modules/cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -1437,7 +1891,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.0" @@ -1544,6 +1997,12 @@ "fsevents": "~2.1.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, "node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -1620,18 +2079,44 @@ "mimic-response": "^1.0.0" } }, - "node_modules/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" + "node_modules/clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "node_modules/cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dependencies": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { "color-name": "1.1.3" @@ -1706,6 +2191,12 @@ "node": ">=8" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, "node_modules/contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -1738,6 +2229,18 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "node_modules/cron-parser": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.18.0.tgz", + "integrity": "sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg==", + "dependencies": { + "is-nan": "^1.3.0", + "moment-timezone": "^0.5.31" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1761,6 +2264,16 @@ "node": ">=8" } }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, "node_modules/cssfilter": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", @@ -1784,7 +2297,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, + "devOptional": true, "dependencies": { "mimic-response": "^1.0.0" }, @@ -1796,7 +2309,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=4.0.0" } @@ -1840,11 +2353,37 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "node_modules/denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/deprecated-decorator": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=" }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1878,6 +2417,16 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dot-prop": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", @@ -1911,7 +2460,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, + "devOptional": true, "dependencies": { "once": "^1.4.0" } @@ -2459,6 +3008,15 @@ "node": ">=0.10.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", @@ -2680,6 +3238,12 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2767,6 +3331,12 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2796,11 +3366,73 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-intrinsic": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -2833,6 +3465,12 @@ "node": ">=6" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "optional": true + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -3034,6 +3672,12 @@ "node": ">= 0.4" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, "node_modules/has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -3043,6 +3687,25 @@ "node": ">=8" } }, + "node_modules/history": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.0.0.tgz", + "integrity": "sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -3055,6 +3718,29 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, + "node_modules/hyperid": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-2.1.0.tgz", + "integrity": "sha512-cSakhxbUsaIuqjfvvcUuvl/Fl342J65xgLLYrYxSSr9qmJ/EJK+S8crS6mIlQd/a7i+Pe4D0MgSrtZPLze+aCw==", + "dependencies": { + "uuid": "^3.4.0", + "uuid-parse": "^1.1.0" + } + }, + "node_modules/hyperid/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true + }, "node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -3104,6 +3790,15 @@ "node": ">=0.8.19" } }, + "node_modules/indefinite-observable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz", + "integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==", + "dev": true, + "dependencies": { + "symbol-observable": "1.2.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3119,13 +3814,10 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true, - "engines": { - "node": "*" - } + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "devOptional": true }, "node_modules/internal-slot": { "version": "1.0.2", @@ -3141,6 +3833,30 @@ "node": ">= 0.4" } }, + "node_modules/ioredis": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.22.0.tgz", + "integrity": "sha512-mtC+jNFMPRxReWx0HodDbcwj34Gj5pK/P4+aE6Nh0pdqgtZKvxUh4z2lVtLjqnRIvMhKaBnIgMYFR8qH/xtttA==", + "dependencies": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.1.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "p-map": "^2.1.0", + "redis-commands": "1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.0.1" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3236,6 +3952,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "dev": true + }, "node_modules/is-installed-globally": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", @@ -3249,6 +3971,21 @@ "node": ">=8" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-negative-zero": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", @@ -3408,6 +4145,97 @@ "json5": "lib/cli.js" } }, + "node_modules/jss": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.1.tgz", + "integrity": "sha512-hbbO3+FOTqVdd7ZUoTiwpHzKXIo5vGpMNbuXH1a0wubRSWLWSBvwvaq4CiHH/U42CmjOnp6lVNNs/l+Z7ZdDmg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "indefinite-observable": "^2.0.1", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.1.tgz", + "integrity": "sha512-9+oymA7wPtswm+zxVti1qiowC5q7bRdCJNORtns2JUj/QHp2QPXYwSNRD8+D2Cy3/CEMtdJzlNnt5aXmpS6NAg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.5.1" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.1.tgz", + "integrity": "sha512-D48hJBc9Tj3PusvlillHW8Fz0y/QqA7MNmTYDQaSB/7mTrCZjt7AVRROExoOHEtd2qIYKOYJW3Jc2agnvsXRlQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.1.tgz", + "integrity": "sha512-jX4XpNgoaB8yPWw/gA1aPXJEoX0LNpvsROPvxlnYe+SE0JOhuvF7mA6dCkgpXBxfTWKJsno7cDSCgzHTocRjCQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.1.tgz", + "integrity": "sha512-xXkWKOCljuwHNjSYcXrCxBnjd8eJp90KVFW1rlhvKKRXnEKVD6vdKXYezk2a89uKAHckSvBvBoDGsfZrldWqqQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.1.tgz", + "integrity": "sha512-t+2vcevNmMg4U/jAuxlfjKt46D/jHzCPEjsjLRj/J56CvP7Iy03scsUP58Iw8mVnaV36xAUZH2CmAmAdo8994g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.1.tgz", + "integrity": "sha512-3gjrSxsy4ka/lGQsTDY8oYYtkt2esBvQiceGBB4PykXxHoGRz14tbCK31Zc6DHEnIeqsjMUGbq+wEly5UViStQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.1.tgz", + "integrity": "sha512-cLkH6RaPZWHa1TqSfd2vszNNgxT1W0omlSjAd6hCFHp3KIocSrW21gaHjlMU26JpTHwkc+tJTCQOmE/O1A4FKQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.5.1" + } + }, "node_modules/jsx-ast-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.1.0.tgz", @@ -3508,8 +4336,17 @@ "node_modules/lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, "node_modules/lodash.get": { "version": "4.4.2", @@ -3639,7 +4476,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, + "devOptional": true, "engines": { "node": ">=4" } @@ -3659,13 +4496,56 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "devOptional": true + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", + "dependencies": { + "moment": ">= 2.9.0" + }, + "engines": { + "node": "*" + } }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "optional": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3680,6 +4560,24 @@ "node": ">= 0.6" } }, + "node_modules/node-abi": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.3.tgz", + "integrity": "sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg==", + "optional": true, + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", @@ -3735,6 +4633,12 @@ "semver": "bin/semver" } }, + "node_modules/noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", + "optional": true + }, "node_modules/nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", @@ -3786,11 +4690,32 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -3939,6 +4864,15 @@ "node": ">= 0.8.0" } }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", @@ -3948,6 +4882,28 @@ "node": ">=6" } }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -3972,6 +4928,25 @@ "node": ">=4" } }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -4114,6 +5089,46 @@ "node": ">=4" } }, + "node_modules/prebuild-install": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.0.tgz", + "integrity": "sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg==", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.2.7", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prebuild-install/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4178,6 +5193,36 @@ "react-is": "^16.8.1" } }, + "node_modules/protobufjs": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", + "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/protobufjs/node_modules/@types/node": { + "version": "13.13.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.41.tgz", + "integrity": "sha512-qLT9IvHiXJfdrje9VmsLzun7cQ65obsBTmtU3EOnCSLFOoSHx1hpiRHoBnpdbyFqnzqdUUIv81JcEJQCB8un9g==" + }, "node_modules/proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -4254,7 +5299,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, + "devOptional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -4269,13 +5314,13 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "devOptional": true }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -4304,15 +5349,67 @@ "scheduler": "^0.20.1" }, "peerDependencies": { - "react": "17.0.1" + "react": "17.0.1" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/react-refresh-typescript": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-1.1.1.tgz", + "integrity": "sha512-4H1FREZ9VYAgsNE5Qlt4SaHAcTn3owXd2IVzWlbt9K+b4chNH10G3XOlIOznsf9oB6zAdxy5VpBdE06xUDm3lw==", + "peerDependencies": { + "typescript": "^4" + } + }, + "node_modules/react-router": { + "version": "6.0.0-beta.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.0.0-beta.0.tgz", + "integrity": "sha512-VgMdfpVcmFQki/LZuLh8E/MNACekDetz4xqft+a6fBZvvJnVqKbLqebF7hyoawGrV1HcO5tVaUang2Og4W2j1Q==", + "dev": true, + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "history": ">=5", + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.0.0-beta.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.0-beta.0.tgz", + "integrity": "sha512-36yNNGMT8RB9FRPL9nKJi6HKDkgOakU+o/2hHpSzR6e37gN70MpOU6QQlmif4oAWWBwjyGc3ZNOMFCsFuHUY5w==", + "dev": true, + "dependencies": { + "prop-types": "^15.7.2", + "react-router": "6.0.0-beta.0" + }, + "peerDependencies": { + "history": ">=5", + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "node_modules/read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -4365,11 +5462,41 @@ "node": ">=8.10.0" } }, + "node_modules/redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, + "node_modules/regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, "node_modules/regexp.prototype.flags": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", @@ -4570,8 +5697,7 @@ "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "node_modules/semver-diff": { "version": "3.1.1", @@ -4587,6 +5713,12 @@ "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz", "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==" }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, "node_modules/set-cookie-parser": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz", @@ -4662,7 +5794,38 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "devOptional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "optional": true, + "dependencies": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -4736,6 +5899,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/snappy": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/snappy/-/snappy-6.3.5.tgz", + "integrity": "sha512-lonrUtdp1b1uDn1dbwgQbBsb5BbaiLeKq+AGwOk2No+en+VvJThwmtztwulEQsLinRF681pBqib0NUZaizKLIA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "bindings": "^1.3.1", + "nan": "^2.14.1", + "prebuild-install": "5.3.0" + } + }, "node_modules/sonic-boom": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.3.0.tgz", @@ -4791,6 +5966,11 @@ "node": "*" } }, + "node_modules/standard-as-callback": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", + "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4978,6 +6158,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz", + "integrity": "sha512-OFFeUXFgwnGOKvEXaSv0D0KQ5ADP0n6g3SVONx6I/85JzNZ3u50FRwB3lVIk1QO2HNdI75tbVzc4Z66Gdp9voA==", + "dev": true + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -4990,6 +6176,15 @@ "node": ">=4" } }, + "node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/table": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", @@ -5027,6 +6222,76 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "optional": true, + "dependencies": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + } + }, + "node_modules/tar-fs/node_modules/pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "optional": true, + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/tar-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/term-size": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", @@ -5055,6 +6320,18 @@ "node": ">=6" } }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "optional": true + }, "node_modules/to-readable-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", @@ -5133,6 +6410,18 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5213,9 +6502,9 @@ "dev": true }, "node_modules/typescript": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", - "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.2.tgz", + "integrity": "sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5396,6 +6685,11 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/uuid-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", + "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==" + }, "node_modules/v8-compile-cache": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", @@ -5443,6 +6737,64 @@ "node": ">= 8" } }, + "node_modules/which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "optional": true + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "optional": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "optional": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -5474,6 +6826,21 @@ "node": ">= 6.4.0" } }, + "node_modules/winston-loki": { + "version": "6.0.0-rc.9", + "resolved": "https://registry.npmjs.org/winston-loki/-/winston-loki-6.0.0-rc.9.tgz", + "integrity": "sha512-xH0wO8fJDC9kcjfWBLBYgHSbL2ecIYVRa0oIQ4rYlK8+PygfHcmwDOUWdoS5k8sm6oVOZGnpdSyEclZUhhwwlA==", + "dependencies": { + "async-exit-hook": "2.0.1", + "btoa": "^1.2.1", + "protobufjs": "^6.8.8", + "snappy": "^6.1.1", + "winston-transport": "^4.3.0" + }, + "optionalDependencies": { + "snappy": "^6.1.1" + } + }, "node_modules/winston-transport": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", @@ -5583,6 +6950,15 @@ "node": ">= 0.10.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "optional": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -5686,6 +7062,15 @@ } } }, + "@babel/runtime": { + "version": "7.13.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.7.tgz", + "integrity": "sha512-h+ilqoX998mRVM5FtB5ijRuHUDVt5l3yfoOi2uh18Z/O3hvyaHQ39NpxVkCIG5yFs+mLq/ewFp8Bss6zmWv6ZA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -5696,6 +7081,49 @@ "kuler": "^2.0.0" } }, + "@emotion/cache": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.1.3.tgz", + "integrity": "sha512-n4OWinUPJVaP6fXxWZD9OUeQ0lY7DvtmtSuqtRWT0Ofo/sBLCVSgb4/Oa0Q5eFxcwablRKjUXqXtNZVyEwCAuA==", + "dev": true, + "requires": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.0.0", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "^4.0.3" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true + }, + "@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==", + "dev": true + }, + "@emotion/sheet": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.1.tgz", + "integrity": "sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g==", + "dev": true + }, + "@emotion/utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", + "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==", + "dev": true + }, + "@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", + "dev": true + }, "@eslint/eslintrc": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", @@ -5738,6 +7166,109 @@ "resolved": "https://registry.npmjs.org/@k-foss/ts-worker/-/ts-worker-1.0.5.tgz", "integrity": "sha512-vCAXlgHDuribOSLXrzMXgjmvlc8fIPbp3qYbRbu56hvJGAhg6WdRV+EWFKrp1WC/rAC0hovFr9gtvnkzXo35Sw==" }, + "@material-ui/core": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-5.0.0-alpha.25.tgz", + "integrity": "sha512-TtU3m3qw0FKtrDHCPmakJ25/20bGPRkLn8qjDkyCUYfg7kG4RLIwgiI0fKnF5PCDu46qL+s233+nVwAh8Iq+qw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styled-engine": "5.0.0-alpha.25", + "@material-ui/styles": "5.0.0-alpha.25", + "@material-ui/system": "5.0.0-alpha.25", + "@material-ui/types": "5.1.7", + "@material-ui/unstyled": "5.0.0-alpha.25", + "@material-ui/utils": "5.0.0-alpha.25", + "@popperjs/core": "^2.4.4", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "csstype": "^3.0.2", + "hoist-non-react-statics": "^3.3.2", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/styled-engine": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/styled-engine/-/styled-engine-5.0.0-alpha.25.tgz", + "integrity": "sha512-z9piABOWycFx1yA9Th8MAs/03SfVOobh+4X1owZmOzXVSln3Xm0C3GjsFmfUpYMC8oC0vddFj00IcCCxm0OECg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/cache": "^11.0.0", + "prop-types": "^15.7.2" + } + }, + "@material-ui/styles": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-5.0.0-alpha.25.tgz", + "integrity": "sha512-VODyi/JT0njmkcyRKiYPacKQmhS11bidg6AMTgAU5KjdmchFPRIl5lwIHFYeRtVEsHX9/9tE0pPlCJIhVYAYsw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "5.1.7", + "@material-ui/utils": "5.0.0-alpha.25", + "clsx": "^1.0.4", + "csstype": "^3.0.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + } + }, + "@material-ui/system": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-5.0.0-alpha.25.tgz", + "integrity": "sha512-4TrsBfB9MMsUj8o1jGRBI4Zv1SSASDwg1lfad5SYtlGhL+0LiSDO/XhVvOvYaRi749lguux2rlrVZpIut0jgMA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "5.0.0-alpha.25", + "csstype": "^3.0.2", + "prop-types": "^15.7.2" + } + }, + "@material-ui/types": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.7.tgz", + "integrity": "sha512-OSpB0gEKZm5h4izTLyipb34PkfazpvusgQMDTmFkSuqcKoChTshfGejEYX6uaZ+4m5xlT5qzihE6eKA+JnjELg==", + "dev": true, + "requires": {} + }, + "@material-ui/unstyled": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/unstyled/-/unstyled-5.0.0-alpha.25.tgz", + "integrity": "sha512-LqAXDhSk2GC3C3SX0U5V3U0WA6gAS5Ayrozo9Br2XtxfEDdPTDmR1R7csT+DruyP3vGBs4k4ILIUz8KfC/kjww==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "5.0.0-alpha.25", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, + "@material-ui/utils": { + "version": "5.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-5.0.0-alpha.25.tgz", + "integrity": "sha512-wgW1ua+ncAU6lrE2bFhd9iU1xmpxj1KL+YRJa0PoFpFgLNVpSTCVHynMlRAERNP+CteazFHj5upkigqJpV406Q==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@types/prop-types": "^15.7.3", + "@types/react-is": "^16.7.1 || ^17.0.0", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, "@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -5764,6 +7295,12 @@ "fastq": "^1.6.0" } }, + "@popperjs/core": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.8.4.tgz", + "integrity": "sha512-h0lY7g36rhjNV8KVHKS3/BEOgfsxu0AiRI8+ry5IFBGEsQFkpjxtcpVc9ndN8zrKUeMZXAWMc7eQMepfgykpxQ==", + "dev": true + }, "@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -5848,6 +7385,14 @@ "@types/node": "*" } }, + "@types/ioredis": { + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.19.3.tgz", + "integrity": "sha512-r9rXLYeZVmvTf0nKDmFYrpBeVzK5B6jgE2ncfkzPOAuRM5RTzKnYdW2wVn+ZONMnFxV6H4R3NrCdRdC3CL4nFA==", + "requires": { + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", @@ -5871,9 +7416,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/node": { - "version": "14.14.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz", - "integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==" + "version": "14.14.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz", + "integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==" }, "@types/node-fetch": { "version": "2.5.7", @@ -5909,6 +7454,24 @@ "@types/react": "*" } }, + "@types/react-is": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.0.tgz", + "integrity": "sha512-A0DQ1YWZ0RG2+PV7neAotNCIh8gZ3z7tQnDJyS2xRPDNtAtSPcJ9YyfMP8be36Ha0kQRzbZCrrTMznA4blqO5g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", @@ -6457,11 +8020,59 @@ "tslib": "^1.10.0" } }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "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", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -6543,6 +8154,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, + "async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==" + }, "async-retry": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz", @@ -6583,6 +8199,57 @@ "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "optional": true, + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "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", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "boxen": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", @@ -6669,6 +8336,48 @@ "fill-range": "^7.0.1" } }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "optional": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "optional": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "optional": true + }, + "bullmq": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-1.14.4.tgz", + "integrity": "sha512-Gx5NX+wA+5E1IReSWuW/ybeq0+JUKUk5BvkiO6QWKowrnJaGJLi8r1rl/VSE7h4reErslLmcXqEvBrpg0yMOwg==", + "requires": { + "@types/ioredis": "^4.16.3", + "cron-parser": "^2.7.3", + "get-port": "^5.0.0", + "ioredis": "^4.17.3", + "lodash": "^4.17.11", + "semver": "^6.3.0", + "tslib": "^1.10.0", + "uuid": "^8.2.0" + } + }, "cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -6705,7 +8414,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.0" @@ -6784,6 +8492,12 @@ "readdirp": "~3.4.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -6847,6 +8561,23 @@ "mimic-response": "^1.0.0" } }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "dev": true + }, + "cluster-key-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz", + "integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==" + }, + "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=", + "optional": true + }, "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", @@ -6924,6 +8655,12 @@ "xdg-basedir": "^4.0.0" } }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -6945,6 +8682,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cron-parser": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.18.0.tgz", + "integrity": "sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg==", + "requires": { + "is-nan": "^1.3.0", + "moment-timezone": "^0.5.31" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6962,6 +8708,16 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, "cssfilter": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", @@ -6985,7 +8741,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, + "devOptional": true, "requires": { "mimic-response": "^1.0.0" } @@ -6994,7 +8750,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true + "devOptional": true }, "deep-is": { "version": "0.1.3", @@ -7026,11 +8782,28 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + }, "deprecated-decorator": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -7057,6 +8830,16 @@ "esutils": "^2.0.2" } }, + "dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "dot-prop": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", @@ -7087,7 +8870,7 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, + "devOptional": true, "requires": { "once": "^1.4.0" } @@ -7505,6 +9288,12 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true + }, "fast-decode-uri-component": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", @@ -7696,6 +9485,12 @@ "flat-cache": "^3.0.4" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -7765,6 +9560,12 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7788,11 +9589,63 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true + }, + "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=", + "optional": true, + "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=", + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, "get-intrinsic": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -7813,6 +9666,12 @@ "pump": "^3.0.0" } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", + "optional": true + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -7965,12 +9824,37 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, "has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true }, + "history": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.0.0.tgz", + "integrity": "sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==", + "dev": true, + "peer": true, + "requires": { + "@babel/runtime": "^7.7.6" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + } + }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -7983,6 +9867,28 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, + "hyperid": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-2.1.0.tgz", + "integrity": "sha512-cSakhxbUsaIuqjfvvcUuvl/Fl342J65xgLLYrYxSSr9qmJ/EJK+S8crS6mIlQd/a7i+Pe4D0MgSrtZPLze+aCw==", + "requires": { + "uuid": "^3.4.0", + "uuid-parse": "^1.1.0" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -8017,6 +9923,15 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indefinite-observable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz", + "integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==", + "dev": true, + "requires": { + "symbol-observable": "1.2.0" + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -8032,10 +9947,10 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "devOptional": true }, "internal-slot": { "version": "1.0.2", @@ -8048,6 +9963,23 @@ "side-channel": "^1.0.2" } }, + "ioredis": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.22.0.tgz", + "integrity": "sha512-mtC+jNFMPRxReWx0HodDbcwj34Gj5pK/P4+aE6Nh0pdqgtZKvxUh4z2lVtLjqnRIvMhKaBnIgMYFR8qH/xtttA==", + "requires": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.1.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "p-map": "^2.1.0", + "redis-commands": "1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.0.1" + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -8116,6 +10048,12 @@ "is-extglob": "^2.1.1" } }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "dev": true + }, "is-installed-globally": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", @@ -8126,6 +10064,15 @@ "is-path-inside": "^3.0.1" } }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, "is-negative-zero": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", @@ -8222,34 +10169,121 @@ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "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" + } + }, + "jss": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.1.tgz", + "integrity": "sha512-hbbO3+FOTqVdd7ZUoTiwpHzKXIo5vGpMNbuXH1a0wubRSWLWSBvwvaq4CiHH/U42CmjOnp6lVNNs/l+Z7ZdDmg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "indefinite-observable": "^2.0.1", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-camel-case": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.1.tgz", + "integrity": "sha512-9+oymA7wPtswm+zxVti1qiowC5q7bRdCJNORtns2JUj/QHp2QPXYwSNRD8+D2Cy3/CEMtdJzlNnt5aXmpS6NAg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.5.1" + } + }, + "jss-plugin-default-unit": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.1.tgz", + "integrity": "sha512-D48hJBc9Tj3PusvlillHW8Fz0y/QqA7MNmTYDQaSB/7mTrCZjt7AVRROExoOHEtd2qIYKOYJW3Jc2agnvsXRlQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1" + } + }, + "jss-plugin-global": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.1.tgz", + "integrity": "sha512-jX4XpNgoaB8yPWw/gA1aPXJEoX0LNpvsROPvxlnYe+SE0JOhuvF7mA6dCkgpXBxfTWKJsno7cDSCgzHTocRjCQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1" + } + }, + "jss-plugin-nested": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.1.tgz", + "integrity": "sha512-xXkWKOCljuwHNjSYcXrCxBnjd8eJp90KVFW1rlhvKKRXnEKVD6vdKXYezk2a89uKAHckSvBvBoDGsfZrldWqqQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.1.tgz", + "integrity": "sha512-t+2vcevNmMg4U/jAuxlfjKt46D/jHzCPEjsjLRj/J56CvP7Iy03scsUP58Iw8mVnaV36xAUZH2CmAmAdo8994g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1" } }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "jss-plugin-rule-value-function": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.1.tgz", + "integrity": "sha512-3gjrSxsy4ka/lGQsTDY8oYYtkt2esBvQiceGBB4PykXxHoGRz14tbCK31Zc6DHEnIeqsjMUGbq+wEly5UViStQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.1", + "tiny-warning": "^1.0.2" + } }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "jss-plugin-vendor-prefixer": { + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.1.tgz", + "integrity": "sha512-cLkH6RaPZWHa1TqSfd2vszNNgxT1W0omlSjAd6hCFHp3KIocSrW21gaHjlMU26JpTHwkc+tJTCQOmE/O1A4FKQ==", "dev": true, "requires": { - "minimist": "^1.2.0" + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.5.1" } }, "jsx-ast-utils": { @@ -8337,8 +10371,17 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, "lodash.get": { "version": "4.4.2", @@ -8437,7 +10480,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true + "devOptional": true }, "minimatch": { "version": "3.0.4", @@ -8451,13 +10494,47 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "devOptional": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", + "requires": { + "moment": ">= 2.9.0" + } }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "optional": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -8469,6 +10546,23 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "node-abi": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.19.3.tgz", + "integrity": "sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg==", + "optional": true, + "requires": { + "semver": "^5.4.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true + } + } + }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", @@ -8509,6 +10603,12 @@ } } }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", + "optional": true + }, "nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", @@ -8550,11 +10650,29 @@ "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", "dev": true }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "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=", + "optional": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "devOptional": true }, "object-inspect": { "version": "1.8.0", @@ -8672,12 +10790,31 @@ "word-wrap": "^1.2.3" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "optional": true + }, "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "requires": { + "p-timeout": "^3.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -8696,6 +10833,19 @@ "p-limit": "^1.1.0" } }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -8802,6 +10952,42 @@ "find-up": "^2.1.0" } }, + "prebuild-install": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.0.tgz", + "integrity": "sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg==", + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.2.7", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8851,6 +11037,33 @@ "react-is": "^16.8.1" } }, + "protobufjs": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz", + "integrity": "sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "13.13.41", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.41.tgz", + "integrity": "sha512-qLT9IvHiXJfdrje9VmsLzun7cQ65obsBTmtU3EOnCSLFOoSHx1hpiRHoBnpdbyFqnzqdUUIv81JcEJQCB8un9g==" + } + } + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -8904,7 +11117,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, + "devOptional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -8916,13 +11129,13 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "devOptional": true }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "devOptional": true } } }, @@ -8953,6 +11166,43 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "react-refresh-typescript": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-1.1.1.tgz", + "integrity": "sha512-4H1FREZ9VYAgsNE5Qlt4SaHAcTn3owXd2IVzWlbt9K+b4chNH10G3XOlIOznsf9oB6zAdxy5VpBdE06xUDm3lw==", + "requires": {} + }, + "react-router": { + "version": "6.0.0-beta.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.0.0-beta.0.tgz", + "integrity": "sha512-VgMdfpVcmFQki/LZuLh8E/MNACekDetz4xqft+a6fBZvvJnVqKbLqebF7hyoawGrV1HcO5tVaUang2Og4W2j1Q==", + "dev": true, + "requires": { + "prop-types": "^15.7.2" + } + }, + "react-router-dom": { + "version": "6.0.0-beta.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.0-beta.0.tgz", + "integrity": "sha512-36yNNGMT8RB9FRPL9nKJi6HKDkgOakU+o/2hHpSzR6e37gN70MpOU6QQlmif4oAWWBwjyGc3ZNOMFCsFuHUY5w==", + "dev": true, + "requires": { + "prop-types": "^15.7.2", + "react-router": "6.0.0-beta.0" + } + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -8993,11 +11243,35 @@ "picomatch": "^2.2.1" } }, + "redis-commands": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz", + "integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, "regexp.prototype.flags": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", @@ -9133,8 +11407,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "semver-diff": { "version": "3.1.1", @@ -9150,6 +11423,12 @@ "resolved": "https://registry.npmjs.org/semver-store/-/semver-store-0.3.0.tgz", "integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg==" }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "optional": true + }, "set-cookie-parser": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz", @@ -9215,7 +11494,24 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true + "devOptional": true + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "optional": true + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "optional": true, + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } }, "simple-swizzle": { "version": "0.2.2", @@ -9275,6 +11571,17 @@ } } }, + "snappy": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/snappy/-/snappy-6.3.5.tgz", + "integrity": "sha512-lonrUtdp1b1uDn1dbwgQbBsb5BbaiLeKq+AGwOk2No+en+VvJThwmtztwulEQsLinRF681pBqib0NUZaizKLIA==", + "optional": true, + "requires": { + "bindings": "^1.3.1", + "nan": "^2.14.1", + "prebuild-install": "5.3.0" + } + }, "sonic-boom": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.3.0.tgz", @@ -9327,6 +11634,11 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, + "standard-as-callback": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.0.1.tgz", + "integrity": "sha512-NQOxSeB8gOI5WjSaxjBgog2QFw55FV8TkS6Y07BiB3VJ8xNTvUYm0wl0s8ObgQ5NhdpnNfigMIKjgPESzgr4tg==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -9488,6 +11800,12 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "stylis": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz", + "integrity": "sha512-OFFeUXFgwnGOKvEXaSv0D0KQ5ADP0n6g3SVONx6I/85JzNZ3u50FRwB3lVIk1QO2HNdI75tbVzc4Z66Gdp9voA==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -9497,6 +11815,12 @@ "has-flag": "^3.0.0" } }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, "table": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", @@ -9529,6 +11853,77 @@ } } }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "optional": true, + "requires": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "optional": true, + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "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", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "term-size": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", @@ -9551,6 +11946,18 @@ "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==" }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "optional": true + }, "to-readable-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", @@ -9614,6 +12021,15 @@ "tslib": "^1.8.1" } }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9672,9 +12088,9 @@ "dev": true }, "typescript": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", - "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==" + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.2.tgz", + "integrity": "sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==" }, "undefsafe": { "version": "2.0.3", @@ -9822,6 +12238,11 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" }, + "uuid-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", + "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==" + }, "v8-compile-cache": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", @@ -9857,6 +12278,54 @@ "isexe": "^2.0.0" } }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "optional": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "optional": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "optional": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -9882,6 +12351,18 @@ "winston-transport": "^4.4.0" } }, + "winston-loki": { + "version": "6.0.0-rc.9", + "resolved": "https://registry.npmjs.org/winston-loki/-/winston-loki-6.0.0-rc.9.tgz", + "integrity": "sha512-xH0wO8fJDC9kcjfWBLBYgHSbL2ecIYVRa0oIQ4rYlK8+PygfHcmwDOUWdoS5k8sm6oVOZGnpdSyEclZUhhwwlA==", + "requires": { + "async-exit-hook": "2.0.1", + "btoa": "^1.2.1", + "protobufjs": "^6.8.8", + "snappy": "^6.1.1", + "winston-transport": "^4.3.0" + } + }, "winston-transport": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", @@ -9964,6 +12445,12 @@ "cssfilter": "0.0.10" } }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "optional": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index c8e628d..0b93f3b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "test": "ts-estest ./src", "prepublishOnly": "npm run build", "build": "tsc -p ./tsconfig.build.json", - "start:debug": "node --loader @k-foss/ts-esnode --experimental-modules --experimental-import-meta-resolve --experimental-specifier-resolution=node --harmony-top-level-await ./", + "start:debug": "node --loader @k-foss/ts-esnode --experimental-modules --experimental-import-meta-resolve --experimental-specifier-resolution=node --enable-source-maps --harmony-top-level-await ./", "dev": "nodemon", "prettier": "prettier --config .prettierrc --check \"src/**/*ts\"", "lint": "eslint ./src --ext .js,.ts" @@ -17,7 +17,8 @@ "devDependencies": { "@k-foss/ts-esnode": "^2.0.0", "@k-foss/ts-estests": "^1.0.6", - "@types/node": "^14.14.25", + "@material-ui/core": "^5.0.0-alpha.25", + "@types/node": "^14.14.31", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@types/workbox-sw": "^4.3.1", @@ -38,8 +39,9 @@ "prettier": "^2.2.1", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-router-dom": "^6.0.0-beta.0", "typedi": "^0.10.0", - "typescript": "^4.1.3" + "typescript": "^4.2.2" }, "author": { "email": "me@kristianjones.dev", @@ -50,13 +52,19 @@ "dependencies": { "@k-foss/ts-worker": "^1.0.5", "apollo-server-fastify": "^3.0.0-alpha.3", + "bullmq": "^1.14.4", "cjstoesm": "^0.1.3", "fastify": "^3.11.0", "fastify-websocket": "^3.0.0", "get-port": "^5.1.1", "graphql": "^15.4.0", + "hyperid": "^2.1.0", + "ioredis": "^4.22.0", + "p-event": "^4.2.0", + "react-refresh-typescript": "^1.1.1", "reflect-metadata": "^0.1.13", "type-graphql": "^1.1.1", - "winston": "^3.3.3" + "winston": "^3.3.3", + "winston-loki": "^6.0.0-rc.9" } } diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 07f969f..b5c9988 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -1,6 +1,7 @@ declare namespace NodeJS { interface ProcessEnv { NODE_ENV: import('../Utils/Environment').Environment; + LOG_LEVEL: keyof typeof import('../Library/Logger').LogLevel; } } diff --git a/src/Library/Apollo.ts b/src/Library/Apollo.ts deleted file mode 100644 index 445ffba..0000000 --- a/src/Library/Apollo.ts +++ /dev/null @@ -1,52 +0,0 @@ -// src/ -import { getGQLContext } from './Context'; - -type ApolloServer = import('apollo-server-fastify').ApolloServer; -type ApolloServerTestClient = import('apollo-server-testing').ApolloServerTestClient; - -let gqlServer: ApolloServer; - -/** - * Create Apollo GraphQL Server - * - * @returns Promise that resolves to a ApolloServer Instance - */ -export async function createApolloServer(): Promise { - if (!gqlServer) { - const [ - { ApolloServer }, - { getResolvers, buildGQLSchema }, - ] = await Promise.all([ - import('apollo-server-fastify'), - import('./Resolvers'), - ]); - - const resolvers = await getResolvers(); - - gqlServer = new ApolloServer({ - schema: await buildGQLSchema(resolvers), - context: getGQLContext, - introspection: true, - playground: { - settings: { - 'editor.theme': 'light', - 'general.betaUpdates': true, - }, - workspaceName: 'TS-ESWeb', - }, - }); - } - - return gqlServer; -} - -/** - * Create a Apollo Server Testing instance - */ -export async function createApolloTestClient(): Promise { - const { createTestClient } = await import('apollo-server-testing'); - - const gqlServer = await createApolloServer(); - - return createTestClient(gqlServer); -} diff --git a/src/Library/Fastify.test.ts b/src/Library/Fastify.test.ts index e99db4f..a04fcd0 100644 --- a/src/Library/Fastify.test.ts +++ b/src/Library/Fastify.test.ts @@ -34,18 +34,12 @@ export class FastifyTestSuite extends TestSuite { 'fastifyRoutes[0].options.method === GET', ); - logger.debug(`HelloTest`); - const webServer = fastify(); - logger.debug(`HelloTest`, webServer); - fastifyRoutes.map((route) => { - logger.debug(`Fastify Route: `, route); - return webServer.route({ ...route.options, - handler: route.handler, + handler: async (...params) => route.handler(...params), }); }); diff --git a/src/Library/Fastify.ts b/src/Library/Fastify.ts index c9c256c..570e7cb 100644 --- a/src/Library/Fastify.ts +++ b/src/Library/Fastify.ts @@ -3,7 +3,6 @@ import fastify, { FastifyInstance, FastifyReply, FastifyRequest, - RouteHandlerMethod, RouteOptions, } from 'fastify'; import fastifyWS from 'fastify-websocket'; @@ -86,7 +85,7 @@ export async function createFastifyServer( routes.map((route) => { return webServer.route({ ...route.options, - handler: async (...params: unknown[]) => route.handler(...params), + handler: async (...params) => route.handler(...params), }); }); diff --git a/src/Library/Logger.ts b/src/Library/Logger.ts index 49829f8..6334c16 100644 --- a/src/Library/Logger.ts +++ b/src/Library/Logger.ts @@ -1,15 +1,55 @@ // src/Library/Logger.ts import * as winston from 'winston'; +import LokiTransport from 'winston-loki'; + +export enum LogLevel { + SILLY = 'silly', + DEBUG = 'debug', + INFO = 'info', + WARN = 'warn', + ERROR = 'error', +} + +function isKeyOf( + obj: Obj, + key: K | string, +): key is K { + return Object.keys(obj).includes(key as string); +} + +function getLevel(value: string): LogLevel { + if (isKeyOf(LogLevel, value)) { + return LogLevel[value]; + } + + return process.env.NODE_ENV === 'production' ? LogLevel.INFO : LogLevel.DEBUG; +} export const logger = winston.createLogger({ - level: 'debug', - format: winston.format.prettyPrint(), - defaultMeta: { service: 'user-service' }, + level: getLevel(process.env.LOG_LEVEL), + defaultMeta: { + appName: 'TS-ESWeb', + labels: { + appName: 'TS-ESWeb', + }, + }, transports: [ // // - Write all logs with level `error` and below to `error.log` // - Write all logs with level `info` and below to `combined.log` // - new winston.transports.Console({}), + new winston.transports.Console({ + level: 'silly', + format: winston.format.prettyPrint({ + colorize: true, + }), + }), + new LokiTransport({ + host: 'http://Loki:3100', + format: winston.format.json({}), + json: true, + level: 'silly', + replaceTimestamp: true, + }), ], }); diff --git a/src/Library/Resolvers.ts b/src/Library/Resolvers.ts index c695909..df932e2 100644 --- a/src/Library/Resolvers.ts +++ b/src/Library/Resolvers.ts @@ -3,6 +3,7 @@ import { buildSchema, NonEmptyArray } from 'type-graphql'; import { findModuleFiles } from '../Utils/moduleFileFinder'; import { GraphQLSchema } from 'graphql'; +import { ContainerInstance } from 'typedi'; type ResolverModule = { [key: string]: Function }; @@ -18,8 +19,10 @@ export async function getResolvers(): Promise { export async function buildGQLSchema( resolvers: Function[], + container: ContainerInstance, ): Promise { return buildSchema({ resolvers: resolvers as NonEmptyArray, + container, }); } diff --git a/src/Library/Transformers.ts b/src/Library/Transformers.ts index 109d969..13bba30 100644 --- a/src/Library/Transformers.ts +++ b/src/Library/Transformers.ts @@ -8,6 +8,8 @@ import { } from 'typescript'; import { findModuleFiles } from '../Utils/moduleFileFinder'; import { cjsToEsmTransformerFactory } from 'cjstoesm'; +import { logger } from './Logger'; +import { ClassType } from 'type-graphql'; let customTransformers: CustomTransformers; @@ -40,6 +42,12 @@ export abstract class Transformer { public afterDeclarations?: TransformerFactory; } +interface TransformersObject { + before: TransformerFactory[]; + + after: TransformerFactory[]; +} + /** * Example Transformer so the findModuleFiles returns non abstract */ @@ -100,3 +108,50 @@ export async function getTransformers( return customTransformers; } + +export class TypeScriptTransformerController { + public transformers: Transformer[]; + + private async findTransformers(): Promise { + const modules = await findModuleFiles<{ + [key: string]: ClassType; + }>(/.*Transformer\.(js|ts)/); + + return modules.flatMap((moduleExports) => + Object.values(moduleExports).map( + (TransformerClass) => new TransformerClass(), + ), + ); + } + + /** + * Load transformer modules and return the transformers object + */ + public async loadTransformers(): Promise { + const transformers = await this.findTransformers(); + + return transformers.reduce( + (accumulator, currentValue) => { + const returned = Object.fromEntries( + (Object.keys(accumulator) as ['before', 'after']).map((key) => + typeof currentValue[key] !== 'undefined' + ? [key, [...accumulator[key], currentValue[key]]] + : [key, accumulator[key]], + ), + ) as TransformersObject; + + logger.silly('transformers reduce: ', { + transformers, + accumulator, + currentValue, + }); + + return returned; + }, + { + before: [cjsToEsmTransformerFactory()], + after: [], + } as TransformersObject, + ); + } +} diff --git a/src/Library/fixtures/Fastify/Routes1/Route1/Fastifty1Route.ts b/src/Library/fixtures/Fastify/Routes1/Route1/Fastifty1Route.ts index 5cc7b95..93c2d0c 100644 --- a/src/Library/fixtures/Fastify/Routes1/Route1/Fastifty1Route.ts +++ b/src/Library/fixtures/Fastify/Routes1/Route1/Fastifty1Route.ts @@ -15,6 +15,7 @@ export default class FastifyRoute1 implements Route { url: '/Route1', }; + // eslint-disable-next-line @typescript-eslint/require-await public async handler( this: FastifyInstance, request: FastifyRequest, diff --git a/src/Modules/Files/processModule.ts b/src/Modules/Files/processModule.ts new file mode 100644 index 0000000..5576ef6 --- /dev/null +++ b/src/Modules/Files/processModule.ts @@ -0,0 +1,88 @@ +// src/Modules/Files/processModule.ts +import { isString } from 'class-validator'; +import { logger } from '../../Library/Logger'; +import { + exportsHandler, + objectExport, + processNodeReplacement, +} from '../TypeScript/Regex'; + +export function processModule(fileContents: string): string { + const exportVars: string[] = []; + + const processLogger = logger.child({ + function: `processModule`, + }); + + processLogger.silly(`Processing module`, { + fileContents, + }); + + let moduleContents: string = fileContents; + + function setModuleContents(newValue: string, actionLabel?: string): void { + processLogger.silly(`setModuleContents`, { + newValue, + actionLabel, + }); + + moduleContents = newValue; + } + + setModuleContents( + moduleContents.replace(processNodeReplacement, '$'), + 'processNodeReplacement', + ); + + /* + setModuleContents( + moduleContents.replace(processNodeIfStatement, '$'), + 'processNodeENVIfStatement', + ); */ + + setModuleContents( + moduleContents.replaceAll( + exportsHandler, + (_test, _todo, varName: string) => { + exportVars.push(varName); + return `var ${varName}`; + }, + ), + 'exportsHandler', + ); + + setModuleContents( + moduleContents.replace(objectExport, (...args) => { + const obj = args[args.length - 1] as { coreCode: string }; + + if (isString(obj.coreCode)) { + return obj.coreCode.replaceAll('exports.', ''); + } + + throw new Error('Invalid object replacement coreCode'); + }), + 'objectExport', + ); + + setModuleContents( + moduleContents.replaceAll( + 'process.env.NODE_ENV', + `'${process.env.NODE_ENV}'`, + ), + ); + + /** + * https://regex101.com/r/uwAq1N/1 + */ + + exportVars.forEach((exportVar) => { + moduleContents = moduleContents.replaceAll( + `exports.${exportVar}`, + `${exportVar}`, + ); + + moduleContents += `exports.${exportVar} = ${exportVar}\n`; + }); + + return moduleContents; +} diff --git a/src/Modules/HMR/HMR.ts b/src/Modules/HMR/HMR.ts deleted file mode 100644 index 15cdefe..0000000 --- a/src/Modules/HMR/HMR.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable @typescript-eslint/no-misused-promises */ -// src/Server/Modules/HMR/HMR.ts -import type { PathLike } from 'fs'; -import { logger } from '../../Library/Logger'; -import { debounce } from '../../Utils/debounce'; -import { BaseEventEmitter } from '../../Utils/Events'; -import { WorkerController } from '../TypeScript/WorkerController'; - -/** - * HMR Class Event Map - */ -interface HMREventsMap { - fileChanged: { - filePath: PathLike; - }; - - moduleUpdated: string; -} - -type WatchedFileEvents = 'change' | string; - -/** - * Hot Module Reload Controller Class - * this controls the HMR features of this system - */ -class HMRController extends BaseEventEmitter { - /** - * Files to be watched for changes, on file change event it is send to a - * transpiler thread to be transpled and the module graph's entry is updated with the returned output code. - */ - public watchedFiles = new Set(); - - /** - * Creates the fs watcher on all HMRed files - */ - public async createWatcher(): Promise { - const fs = await import('fs'); - - const workerController = await WorkerController.spawnWorkers(2, { - cache: false, - }); - workerController.startPolling(); - - Array.from(this.watchedFiles).map((filePath) => { - const watcher = fs.watch(filePath); - - logger.debug( - `HMRController.createWatcher() watching ${filePath.toString()}`, - ); - - watcher.on( - 'change', - debounce((eventType: WatchedFileEvents) => { - switch (eventType) { - case 'change': - this.emit('fileChanged', { - filePath, - }); - } - }, 1500), - ); - - return watcher; - }); - - this.on('fileChanged', async ({ filePath }) => { - logger.info( - `HMRController Spawned threads. Starting jobs with ${filePath.toString()} as the entrypoint`, - ); - - return workerController - .forceAddJob(filePath.toString()) - .then(() => this.emit('moduleUpdated', filePath.toString())); - }); - } -} - -export const HMR = new HMRController(); diff --git a/src/Modules/HMR/index.ts b/src/Modules/HMR/index.ts deleted file mode 100644 index 75634b2..0000000 --- a/src/Modules/HMR/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// src/Server/Modules/HMR/index.ts -export { HMR } from './HMR'; diff --git a/src/Modules/Queues/Queue.ts b/src/Modules/Queues/Queue.ts new file mode 100644 index 0000000..b24c944 --- /dev/null +++ b/src/Modules/Queues/Queue.ts @@ -0,0 +1,307 @@ +// src/Modules/Queue/Queue.ts +import { spawnWorker } from '@k-foss/ts-worker'; +import { + Job, + JobsOptions, + Queue as BullMQQueue, + QueueEvents as BullMQQueueEvents, +} from 'bullmq'; +import { ClassConstructor, plainToClass } from 'class-transformer'; +import EventEmitter from 'events'; +import { fileURLToPath } from 'node:url'; +import pEvent from 'p-event'; +import { Logger } from 'winston'; +import { Worker } from 'worker_threads'; +import { logger } from '../../Library/Logger'; +import { QueueOptions } from './QueueOptions'; +import { WorkerInput } from './WorkerInput'; + +/** + * Task Queue handled by dedicated `worker_threads` created by + * [`@K-FOSS/TS-ESWorkers`](https://www.npmjs.com/package/@k-foss/ts-worker) + * + * The Queue is handled by [`bullmq`](https://www.npmjs.com/package/bullmq) with + * a Redis backend + */ +export class Queue { + /** + * BullMQ Queue and QueueEvents object + */ + public queue: BullMQQueue; + public queueEvents: BullMQQueueEvents; + + private events = new EventEmitter(); + + /** + * Array of Child Workers + */ + private workers: Worker[] = []; + + /** + * Boolean value if a single job has been created. + */ + private hasRun = false; + + /** + * Node.JS Interval to check if there are any active jobs if not, + * and a job has already been created, the child workers are exited. + */ + public checkInterval: NodeJS.Timeout; + + public options: QueueOptions>; + + /** + * Local logger object adding additional metadata + */ + private logger: Logger; + + public constructor( + options: QueueOptions>, + ) { + this.queue = new BullMQQueue(options.name, options.bullOptions); + this.queueEvents = new BullMQQueueEvents(options.name, options.bullOptions); + + this.options = options; + + this.logger = logger.child({ + queName: this.queue.name, + }); + + this.handleError = (...args): void => { + this.logger.info(`Que has errored`, { + ...args, + }); + }; + + this.queueEvents.on('failed', this.handleError); + } + + private handleError: ( + args: { + jobId: string; + failedReason: string; + prev?: string; + }, + id: string, + ) => void; + + /** + * Terminate all worker threads. + */ + private async terminateWorkers(): Promise { + const workerTeminiations = this.workers.map((worker) => worker.terminate()); + + // this.queueEvents.removeListener('drained', this.handleDrained); + + this.events.emit('done'); + + return Promise.all(workerTeminiations); + } + + /** + * Clean all jobs from Redis/BullMQ + */ + private async cleanQueue(): Promise { + this.logger.debug(`Queue.cleanQueue()`); + + return Promise.all([ + this.queue.clean(0, 1000, 'active'), + this.queue.clean(0, 1000, 'failed'), + this.queue.clean(0, 1000, 'completed'), + this.queue.clean(0, 1000, 'wait'), + this.queue.clean(0, 1000, 'paused'), + this.queue.clean(0, 1000), + ]); + } + + private async getCounts(): Promise { + return Promise.all([ + this.queue.getDelayedCount(), + this.queue.getActiveCount(), + this.queue.getFailedCount(), + ]); + } + + /** + * Check if there are any active jobs/tasks + */ + // private async isRunningJobs(): Promise { + // const jobCounts = await this.getCounts(); + + // this.logger.silly(`jobCounts: `, { + // jobCounts, + // }); + + // const anyJobs = jobCounts.some((jobCount) => jobCount > 0); + + // if (this.hasRun === false) { + // if (anyJobs === true) { + // this.hasRun = true; + // } + + // return; + // } + + // if (anyJobs === false) { + // this.logger.silly('Terminating workers'); + + // return this.terminateWorkers(); + // } + // } + + // private handleDrained: () => void; + + /** + * Start watching workers for active tasks and kill workers upon empty Queue + */ + private startWatching(): void { + if (this.options.disableTermination !== true) { + let jobLessCounts = 0; + + const helloWorld = async (): Promise => { + const jobCounts = await this.getCounts(); + + const anyJobs = jobCounts.some((jobCount) => jobCount > 0); + + if (this.hasRun === false) { + if (anyJobs === true) { + this.hasRun = true; + } + + return; + } + + if (anyJobs === false) { + jobLessCounts++; + + if (jobLessCounts > 5) { + return this.terminateWorkers(); + } + } + }; + + setInterval(() => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + helloWorld(); + }, 500); + } + } + + /** + * + * @param workerPath Absolute path to the Worker File + * + * @example + * ```ts + * const moduleMapQueue = new Queue('randomQueue'); + * + * const workerPathURI = await import.meta.resolve( + * './TypeScriptModuleMapWorker', + * ); + * const workerPath = fileURLToPath(workerPathURI); + * + * await moduleMapQueue.createWorkers(workerPath); + * ``` + * + * @returns Promise resolving once the workers threads have all been created + */ + public async createWorkers( + subWorkerPath: string, + workerCount = 2, + ): Promise { + this.logger.debug( + `Queue.createWorkers('${subWorkerPath}', ${workerCount})`, + ); + + this.logger.debug(`Queue.createWorkers() cleaning old jobs`); + + await this.cleanQueue(); + + const workerInputParams: WorkerInput = { + serverOptions: this.options.serverOptions, + queueOptions: this.queue.opts, + queName: this.queue.name, + workerCount, + workerPath: subWorkerPath, + }; + + this.logger.silly(`workerInputParams: `, { + workerInputParams, + }); + + const workerInput = plainToClass(WorkerInput, workerInputParams); + + const workerPathURI = await import.meta.resolve('./Worker'); + const workerPath = fileURLToPath(workerPathURI); + + for (const _workerThread of Array(workerInputParams.workerCount).fill(0)) { + this.logger.info(`Queue.createWorkers() spawning worker`); + + const worker = spawnWorker(workerPath, workerInput); + + worker.on('error', (err) => { + this.logger.error(`Worker thread has errored`, { + err, + }); + + this.logger.info(`TODO: Proper worker error handling`); + }); + + this.workers.push(worker); + } + + return this.startWatching(); + } + + /** + * Create a new Job/Task for the queue + * @param input Job Input data + * + * @example + * ```ts + * interface ModuleMapJobInput { + * redisHost: string; + * filePath: string; + * } + * + * const moduleMapQueue = new Queue('randomQueue'); + * + * const workerPathURI = await import.meta.resolve( + * './ModuleMapWorker', + * ); + * const workerPath = fileURLToPath(workerPathURI); + * + * await moduleMapQueue.createWorkers(workerPath); + * + * const jobInput: ModuleMapJobInput = { + * redisHost: 'redis', + * filePath: '/workspace/src/index.ts' + * } + * + * await moduleMapQueue.addTask(jobInput) + * ``` + * + * @returns BullMQ Job object + */ + public async addTask(input: JobInput, opts?: JobsOptions): Promise { + const job = await this.queue.add(this.options.name, input, opts); + + this.logger.debug(`Queue.addTask(${JSON.stringify(input)})`); + + return job; + } + + /** + * Wait for the result of the specified task + * @param task BullMQ Job entity + */ + public async waitForTask(task: Job): Promise { + this.logger.silly(`waitForTask()`); + + return task.waitUntilFinished(this.queueEvents) as Promise; + } + + public async waitForDone(): Promise { + return pEvent(this.events, 'done'); + } +} diff --git a/src/Modules/Queues/QueueController.ts b/src/Modules/Queues/QueueController.ts new file mode 100644 index 0000000..b9657ab --- /dev/null +++ b/src/Modules/Queues/QueueController.ts @@ -0,0 +1,43 @@ +// src/Modules/Ques/QueueController.ts +import type { QueueOptions as BullMQQueueOptions } from 'bullmq'; +import { ClassConstructor } from 'class-transformer'; +import { Inject, Service } from 'typedi'; +import { ServerOptions, serverOptionsToken } from '../Server/ServerOptions'; +import { Queue } from './Queue'; +import { QueueOptions } from './QueueOptions'; + +@Service() +export class QueueController { + @Inject(serverOptionsToken) + public options: ServerOptions; + + /** + * Create the IORedis Options + */ + private createBullOptions(): BullMQQueueOptions { + return { + connection: { + ...this.options.redis, + }, + }; + } + + /** + * Create a new Queue + * @param queKey Queue key + */ + public createQueue( + input: Omit< + QueueOptions>, + 'bullOptions' | 'serverOptions' + >, + ): Queue { + const bullOptions = this.createBullOptions(); + + return new Queue({ + ...input, + bullOptions, + serverOptions: this.options, + }); + } +} diff --git a/src/Modules/Queues/QueueOptions.ts b/src/Modules/Queues/QueueOptions.ts new file mode 100644 index 0000000..275c42b --- /dev/null +++ b/src/Modules/Queues/QueueOptions.ts @@ -0,0 +1,20 @@ +// src/Modules/Queues/QueueOptions.ts +import type { QueueOptions as BullMQOptions } from 'bullmq'; +import { IsBoolean, IsDefined, IsOptional, IsString } from 'class-validator'; +import { ServerOptions } from '../Server/ServerOptions'; + +export class QueueOptions { + @IsString() + @IsDefined() + public name: QueueName; + + public serverOptions: ServerOptions; + + public bullOptions: BullMQOptions; + + public inputClass: JobInput; + + @IsBoolean() + @IsOptional() + public disableTermination?: boolean; +} diff --git a/src/Modules/Queues/QueueToken.ts b/src/Modules/Queues/QueueToken.ts new file mode 100644 index 0000000..95885c4 --- /dev/null +++ b/src/Modules/Queues/QueueToken.ts @@ -0,0 +1,9 @@ +// src/Modules/Queues/QueueToken.ts +import { Token } from 'typedi'; + +/** + * TypeDI Token for the label/name/key of the Workers Queue + * + * @default queueToken + */ +export const queueToken = new Token('queueToken'); diff --git a/src/Modules/Queues/Worker.ts b/src/Modules/Queues/Worker.ts new file mode 100644 index 0000000..755d7b6 --- /dev/null +++ b/src/Modules/Queues/Worker.ts @@ -0,0 +1,22 @@ +// src/Modules/Queues/Worker.ts +import { getWorkerData } from '@k-foss/ts-worker'; +import { logger } from '../../Library/Logger'; +import '../../Utils/Setup'; +import { WorkerController } from './WorkerController'; +import { WorkerInput } from './WorkerInput'; + +const workerInput = await WorkerInput.createWorkerInput( + getWorkerData(import.meta.url), +); + +logger.silly(`workerInput`, { + workerInput, +}); + +const workerController = await WorkerController.createWorkerController( + workerInput, +); + +workerController.logger.info(`Worker has been spawned`); + +await import(workerInput.workerPath); diff --git a/src/Modules/Queues/WorkerController.ts b/src/Modules/Queues/WorkerController.ts new file mode 100644 index 0000000..917e273 --- /dev/null +++ b/src/Modules/Queues/WorkerController.ts @@ -0,0 +1,75 @@ +// src/Modules/Queues/WorkerController.ts + +import { plainToClass } from 'class-transformer'; +import { validateOrReject } from 'class-validator'; +import { basename } from 'node:path'; +import Container, { Inject, Service, Token } from 'typedi'; +import { Logger } from 'winston'; +import { logger as coreLogger } from '../../Library/Logger'; +import { ServerOptions, serverOptionsToken } from '../Server/ServerOptions'; +import { queueToken } from './QueueToken'; +import { WorkerInput, workerInputToken } from './WorkerInput'; + +@Service() +export class WorkerController { + public logger: Logger; + + public constructor( + @Inject(workerInputToken) + public workerInput: WorkerInput, + ) { + this.logger = coreLogger.child({ + labels: { + appName: 'TS-ESWeb', + worker: basename(workerInput.workerPath), + queueName: workerInput.queName, + }, + }); + + this.logger.silly(`WorkerController created`); + } + + public static async createWorkerController( + workerInput: WorkerInput, + ): Promise { + const serverOptions = plainToClass( + ServerOptions, + workerInput.serverOptions, + ); + await validateOrReject(serverOptions); + + Container.set({ + id: serverOptionsToken, + global: true, + value: serverOptions, + }); + + Container.set({ + id: queueToken, + global: true, + value: workerInput.queName, + }); + + Container.set({ + id: workerInputToken, + global: true, + value: workerInput, + }); + + const workerController = Container.get(WorkerController); + Container.set({ + id: workerControllerToken, + global: true, + value: workerController, + }); + + return Container.get(workerControllerToken); + } +} + +/** + * TypeDI Token for the current workers WorkerController + */ +export const workerControllerToken = new Token( + 'workerController', +); diff --git a/src/Modules/Queues/WorkerInput.ts b/src/Modules/Queues/WorkerInput.ts new file mode 100644 index 0000000..a542cc4 --- /dev/null +++ b/src/Modules/Queues/WorkerInput.ts @@ -0,0 +1,68 @@ +// src/Modules/TypeScript/WorkerInput.ts +import type { QueueOptions } from 'bullmq'; +import { plainToClass, Transform } from 'class-transformer'; +import { IsInt, IsString, validateOrReject } from 'class-validator'; +import { Token } from 'typedi'; +import { logger } from '../../Library/Logger'; +import { ServerOptions } from '../Server/ServerOptions'; + +export class WorkerInput { + @Transform(({ value }) => { + if (typeof value === 'string') { + logger.silly(`WorkerInput.serverOptions is string`, { + value, + }); + + return JSON.parse(value) as ServerOptions; + } + + return JSON.stringify(value); + }) + public serverOptions: ServerOptions; + + @Transform(({ value }) => { + if (typeof value === 'string') { + logger.silly(`WorkerInput.queueOptions is string`, { + value, + }); + + return JSON.parse(value) as QueueOptions; + } + + return JSON.stringify(value); + }) + public queueOptions: QueueOptions; + + @IsString() + public workerPath: string; + + /** + * Name of the TypeScript Transpiler Que for this worker + */ + @IsString() + public queName: string; + + /** + * Number of workers spawned + */ + @IsInt() + @Transform(({ value }) => { + if (typeof value === 'number') { + return `${value}`; + } + + return parseInt(value); + }) + public workerCount: number; + + public static async createWorkerInput( + inputParams: Partial, + ): Promise { + const jobInput = plainToClass(WorkerInput, inputParams); + await validateOrReject(jobInput); + + return jobInput; + } +} + +export const workerInputToken = new Token('workerInput'); diff --git a/src/Modules/Redis/RedisController.ts b/src/Modules/Redis/RedisController.ts new file mode 100644 index 0000000..ac62ada --- /dev/null +++ b/src/Modules/Redis/RedisController.ts @@ -0,0 +1,145 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +// src/Modules/Redis/RedisController.ts +import { plainToClass } from 'class-transformer'; +import { + isNumberString, + isString, + IsString, + validateOrReject, +} from 'class-validator'; +import IORedis from 'ioredis'; +import { Inject, Service } from 'typedi'; +import { logger } from '../../Library/Logger'; +import { ServerOptions, serverOptionsToken } from '../Server/ServerOptions'; +import { RedisType } from './RedisTypes'; + +export class SetValueInput { + @IsString() + public key: string; + + @IsString({ + each: true, + }) + public value: string | string[]; +} + +@Service() +export class RedisController { + /** + * Redis Instance + */ + public IORedis: IORedis.Redis; + + private async clearRedis(): Promise> { + logger.debug('Cleaning up redis'); + + const pipeline = this.IORedis.pipeline(); + + for (const redisTypeKey in RedisType) { + if (isNumberString(redisTypeKey) === false) { + const redisKeyPrefix = this.getRedisKeyPrefix( + RedisType[redisTypeKey as keyof typeof RedisType], + ); + + logger.debug(`redisKeyPrefix: `, { + redisKeyPrefix, + }); + + const redisKeys = await this.IORedis.keys(`${redisKeyPrefix}-*`); + + for (const redisKey of redisKeys) { + pipeline.del(redisKey); + } + } + } + + return pipeline.exec(); + } + + public constructor(@Inject(serverOptionsToken) options: ServerOptions) { + this.IORedis = new IORedis(options.redis); + + process.on('exit', () => { + // Do some cleanup such as close db + this.clearRedis(); + }); + + // catching signals and do something before exit + [ + 'SIGHUP', + 'SIGINT', + 'SIGQUIT', + 'SIGILL', + 'SIGTRAP', + 'SIGABRT', + 'SIGBUS', + 'SIGFPE', + 'SIGUSR1', + 'SIGSEGV', + 'SIGUSR2', + 'SIGTERM', + ].forEach(function (sig) { + process.on(sig, function () { + terminator(sig); + console.log('signal: ' + sig); + }); + }); + + const terminator = async (sig: string | number): Promise => { + if (typeof sig === 'string') { + // call your async task here and then call process.exit() after async task is done + await this.clearRedis(); + } + }; + } + + private getRedisKeyPrefix(type: RedisType): string { + switch (type) { + case RedisType.MODULE: + return 'module'; + case RedisType.MODULE_MAP: + return 'moduleMap'; + } + } + + public getRedisKey(type: RedisType, key: string): string { + return `${this.getRedisKeyPrefix(type)}-${key}`; + } + + /** + * Set a value in Redis + * @param type Redis DB Type + * @param input Input + */ + public async setValue( + type: RedisType, + inputObj: SetValueInput, + ): Promise { + const input = plainToClass(SetValueInput, inputObj); + await validateOrReject(input); + + await this.IORedis.set(this.getRedisKey(type, input.key), input.value); + } + + /** + * Get a value for a type and key in Redis + */ + public async getValue(type: RedisType, key: string): Promise { + const value = await this.IORedis.get(this.getRedisKey(type, key)); + + return value; + } + + public async getValueOrThrow(type: RedisType, key: string): Promise { + const value = await this.getValue(type, key); + + if (isString(value)) { + return value; + } + + logger.error( + `RedisController.getValue(${type}, ${key}) returned value is invalid`, + ); + throw new Error(`Invalid value from Redis for key ${key}`); + } +} diff --git a/src/Modules/Redis/RedisOptions.ts b/src/Modules/Redis/RedisOptions.ts new file mode 100644 index 0000000..43355f0 --- /dev/null +++ b/src/Modules/Redis/RedisOptions.ts @@ -0,0 +1,11 @@ +// src/Modules/Redis/RedisOptions.ts +import { IsDefined, IsString } from 'class-validator'; +import { Token } from 'typedi'; + +export class RedisOptions { + @IsString() + @IsDefined() + public host: string; +} + +export const redisOptionsToken = new Token('redisOptions'); diff --git a/src/Modules/Redis/RedisTypes.ts b/src/Modules/Redis/RedisTypes.ts new file mode 100644 index 0000000..7f22b16 --- /dev/null +++ b/src/Modules/Redis/RedisTypes.ts @@ -0,0 +1,5 @@ +// src/Modules/Redis/RedisTypes.ts +export enum RedisType { + MODULE_MAP, + MODULE, +} diff --git a/src/Modules/SSR/SSRController.tsx b/src/Modules/SSR/SSRController.tsx new file mode 100644 index 0000000..1e002c0 --- /dev/null +++ b/src/Modules/SSR/SSRController.tsx @@ -0,0 +1,24 @@ +// src/Modules/SSR/SSRController.tsx + +import { renderToString } from 'react-dom/server'; +import { StaticRouter } from './StaticRouter'; +import { Inject, Service } from 'typedi'; +import { ServerOptions, serverOptionsToken } from '../Server/ServerOptions'; + +@Service() +export class SSRController { + @Inject(serverOptionsToken) + public options: ServerOptions; + + public renderApp(path: string): string { + const App = this.options.ssr.appComponent; + + console.log(this.options, App); + + return renderToString( + + + , + ); + } +} diff --git a/src/Modules/SSR/SSROptions.ts b/src/Modules/SSR/SSROptions.ts new file mode 100644 index 0000000..e57248f --- /dev/null +++ b/src/Modules/SSR/SSROptions.ts @@ -0,0 +1,27 @@ +// src/Modules/SSR/SSROptions.ts +import { Exclude } from 'class-transformer'; +import { IsDefined, IsString } from 'class-validator'; + +export class SSROptions { + /** + * Root Directory for Web/Client + * + * @example + * ```ts + * webRoot: path.resolve('./Web') + * ``` + */ + @IsString() + @IsDefined() + public webRoot: string; + + /** + * Entrypoint Path relative to @see webRoot + */ + @IsString() + @IsDefined() + public entrypoint: string; + + @Exclude() + public appComponent: () => React.ReactElement; +} diff --git a/src/Modules/SSR/SSRRoute.ts b/src/Modules/SSR/SSRRoute.ts deleted file mode 100644 index 4713244..0000000 --- a/src/Modules/SSR/SSRRoute.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-types */ -// src/Modules/SSR/SSRRoute.ts -import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; -import { Service } from 'typedi'; -import { Route } from '../../Library/Fastify'; -import { ssrServer } from '../Server/createSSRServer'; - -/** - * Route to serve the rendered React SSR Routes - */ -@Service() -export default class SSRRoute implements Route { - public options: Route['options'] = { - method: 'GET', - url: '/Test', - }; - - public async handler( - this: FastifyInstance, - request: FastifyRequest, - reply: FastifyReply, - ): Promise { - reply.type('text/html'); - - const appHTML = ssrServer.renderApp(request.url); - - return appHTML; - } -} diff --git a/src/Modules/SSR/StaticRouter.tsx b/src/Modules/SSR/StaticRouter.tsx new file mode 100644 index 0000000..71a34ee --- /dev/null +++ b/src/Modules/SSR/StaticRouter.tsx @@ -0,0 +1,93 @@ +import * as React from 'react'; +import { + Action, + Location, + PartialLocation, + To, + createPath, + parsePath, +} from 'history'; +import { Router } from 'react-router-dom'; + +export interface StaticRouterProps { + children?: React.ReactNode; + location?: string | PartialLocation; +} + +/** + * A that may not transition to any other location. This is useful + * on the server where there is no stateful UI. + */ +export function StaticRouter({ + children, + location: loc = '/', +}: StaticRouterProps) { + if (typeof loc === 'string') { + loc = parsePath(loc); + } + + let action = Action.Pop; + let location: Location = { + pathname: loc.pathname || '/', + search: loc.search || '', + hash: loc.hash || '', + state: loc.state || null, + key: loc.key || 'default', + }; + + let staticNavigator = { + createHref(to: To) { + return typeof to === 'string' ? to : createPath(to); + }, + push(to: To) { + throw new Error( + `You cannot use navigator.push() on the server because it is a stateless ` + + `environment. This error was probably triggered when you did a ` + + `\`navigate(${JSON.stringify(to)})\` somewhere in your app.`, + ); + }, + replace(to: To) { + throw new Error( + `You cannot use navigator.replace() on the server because it is a stateless ` + + `environment. This error was probably triggered when you did a ` + + `\`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere ` + + `in your app.`, + ); + }, + go(delta: number) { + throw new Error( + `You cannot use navigator.go() on the server because it is a stateless ` + + `environment. This error was probably triggered when you did a ` + + `\`navigate(${delta})\` somewhere in your app.`, + ); + }, + back() { + throw new Error( + `You cannot use navigator.back() on the server because it is a stateless ` + + `environment.`, + ); + }, + forward() { + throw new Error( + `You cannot use navigator.forward() on the server because it is a stateless ` + + `environment.`, + ); + }, + block() { + throw new Error( + `You cannot use navigator.block() on the server because it is a stateless ` + + `environment.`, + ); + }, + }; + + return ( + + ); +} diff --git a/src/Modules/Server/SSRServer.tsx b/src/Modules/Server/SSRServer.tsx deleted file mode 100644 index bb4a1d2..0000000 --- a/src/Modules/Server/SSRServer.tsx +++ /dev/null @@ -1,172 +0,0 @@ -// src/Modules/Server/SSRServer.ts -import fastify, { FastifyInstance } from 'fastify'; -import { resolve } from 'path'; -import React from 'react'; -import { renderToString } from 'react-dom/server'; -import Container from 'typedi'; -import { createApolloServer } from '../../Library/Apollo'; -import { getRoutes, Route } from '../../Library/Fastify'; -import { logger } from '../../Library/Logger'; -import { ReactFunction } from '../../Utils/React'; -import { startWebTranspiler } from '../TypeScript'; - -export interface ClientOptions { - appComponent: ReactFunction; - - /** - * Root Directory for Web/Client - * - * @example - * ```ts - * webRoot: path.resolve('./Web') - * ``` - */ - webRoot: string; - - /** - * Entrypoint Path relative to @see webRoot - */ - entrypoint: string; - - /** - * Additional Fastify Routes - */ - serverRoutes: Route[]; -} - -/** - * TS-ESWeb SSR Server Controller - */ -export class SSRServer { - /** - * Client Provided Options - */ - public options: ClientOptions = { - appComponent: () =>
HelloWorld
, - entrypoint: 'Imports.ts', - webRoot: 'Web', - serverRoutes: [], - }; - - public webServer: FastifyInstance = fastify(); - - public get entrypoint(): string { - return resolve(this.options.webRoot, this.options.entrypoint); - } - - /** - * Get Absolute Path from a relative path - * @param relativePath Relative Path to Web Root - */ - public getFilePath(relativePath: string): string { - return resolve(this.options.webRoot, relativePath); - } - - /** - * Create a new SSRServer Instance - * @param opt Properties of SSRServer - */ - public constructor(opt: ClientOptions) { - this.options = opt; - } - - /** - * Start transpiling Modules from Imports - */ - public async startTranspiler(): Promise { - logger.info(`Starting transpiler for ${this.entrypoint}`); - - logger.debug( - `SSRServer.startTranspiler() entrypoint: ${this.entrypoint} this.options.webRoot: ${this.options.webRoot}`, - ); - - await startWebTranspiler(this.entrypoint); - } - - public async createFastifyServer(): Promise { - /** - * Get All Route Modules. - */ - const routes = await getRoutes(Container.of()); - - /** - * For each Route Module in routes destructure handler and options and register as a webServer Route. - */ - routes.map((route) => { - return this.webServer.route({ - ...route.options, - handler: async (...params: unknown[]) => route.handler(...params), - }); - }); - - /** - * Apollo GraphQL Server - */ - const gqlServer = await createApolloServer(); - - const apiPlugin = gqlServer.createHandler(); - - /** - * Register the Apollo Server Routes into the Fastify instance - */ - await this.webServer.register(apiPlugin); - - return this.webServer; - } - - public renderApp(path: string): string { - const App = this.options.appComponent; - - logger.debug(`SSRServer.renderApp(${path})`); - - const appHTML = renderToString(); - - const coreHTML = ` - - TS-ESWeb - - - -
${appHTML}
- - - `; - - return coreHTML; - } -} diff --git a/src/Modules/Server/ServerController.ts b/src/Modules/Server/ServerController.ts new file mode 100644 index 0000000..beb8700 --- /dev/null +++ b/src/Modules/Server/ServerController.ts @@ -0,0 +1,246 @@ +// src/Modules/Server/ServerController.ts +import { ApolloServer } from 'apollo-server-fastify'; +import { classToPlain, plainToClass } from 'class-transformer'; +import { validateOrReject } from 'class-validator'; +import hyperid from 'hyperid'; +import { resolve } from 'node:path'; +import { Container, Inject, Service } from 'typedi'; +import { fileURLToPath } from 'url'; +import { getGQLContext } from '../../Library/Context'; +import { logger } from '../../Library/Logger'; +import { buildGQLSchema, getResolvers } from '../../Library/Resolvers'; +import { timeout } from '../../Utils/timeout'; +import { RedisController } from '../Redis/RedisController'; +import { RedisType } from '../Redis/RedisTypes'; +import { SSRController } from '../SSR/SSRController'; +import { TypeScriptController } from '../TypeScript/TypeScriptController'; +import { WebAppManifest } from '../WebAppManifest/WebAppManifest'; +import { WebModuleController } from '../WebModule/WebModuleController'; +import { WebModuleJobInput } from '../WebModule/WebModuleJobInput'; +import { WebModuleMapJobInput } from '../WebModule/WebModuleMapJobInput'; +import { ServerOptions, serverOptionsToken } from './ServerOptions'; + +@Service() +export class ServerController { + @Inject() + public redisController: RedisController; + + @Inject() + public ssrController: SSRController; + + /** + * Path of the Web App Manifest + */ + private webAppManifestPath = '/Main.webmanifest'; + + // eslint-disable-next-line no-useless-constructor + public constructor( + private typescriptController: TypeScriptController, + private webModuleController: WebModuleController, + @Inject(serverOptionsToken) + public options: ServerOptions, + ) {} + + private getPath(relativePath: string): string { + return resolve(this.options.ssr.webRoot, relativePath); + } + + public async createApolloServer(): Promise { + const resolvers = await getResolvers(); + + const schema = await buildGQLSchema( + resolvers, + Container.of(this.options.serverId), + ); + + const gqlServer = new ApolloServer({ + schema, + context: getGQLContext, + introspection: true, + playground: { + settings: { + 'editor.theme': 'light', + 'general.betaUpdates': true, + }, + workspaceName: 'TS-ESWeb', + }, + }); + + return gqlServer; + } + + /** + * + * @param options + * @returns + */ + public handleWebAppManifest(): WebAppManifest { + return classToPlain(this.options.manifest) as WebAppManifest; + } + + /** + * Create a new ServerController with the provided Configuration within the TypeDi Container + * @param options Server Configuration + * @param container Optional TypeDi Container defaults to `Container.of()` + * @returns Promise resolving to newly configured ServerController class + */ + public static async createServer( + options: Partial, + ): Promise { + logger.info(`ServerController.createServer()`); + + const serverId = hyperid().uuid; + + const container = Container.of(serverId); + + const serverOptions = plainToClass( + ServerOptions, + { + ...options, + serverId, + }, + { + enableCircularCheck: true, + }, + ); + logger.silly('serverOptions post transform: ', { + serverOptions, + }); + + await validateOrReject(serverOptions); + + if ( + typeof serverOptions.ssr !== 'undefined' && + typeof options.ssr?.appComponent !== 'undefined' + ) { + serverOptions.ssr.appComponent = options.ssr.appComponent; + } + + container.set({ + id: serverOptionsToken, + global: true, + value: serverOptions, + }); + + return container.get(ServerController); + } + + public async getModuleMap(filePath: string): Promise { + const result = await this.redisController.getValue( + RedisType.MODULE_MAP, + filePath, + ); + + if (typeof result === 'string') { + return plainToClass(WebModuleJobInput, JSON.parse(result)); + } + + throw new Error('Invalid result from Redis'); + } + + public async getPathModule(filePath: string): Promise { + logger.silly('HelloWorld'); + + const result = await this.redisController.getValue( + RedisType.MODULE, + filePath, + ); + + return result as string; + } + + public async startTypeScript(): Promise { + logger.info( + `ServerController.startTypeScript() starting TypeScript Module Map`, + ); + + await Promise.all([ + this.typescriptController.createModuleMapWorkers(), + this.webModuleController.spawnWebModuleWorkers(), + this.typescriptController.createTranspilerWorkers(), + ]); + + const filePath = fileURLToPath( + await import.meta.resolve('../../Web_Test/Imports.ts'), + ); + + await this.typescriptController.createModuleMapTask({ + filePath, + }); + logger.silly(`entrypointJob created`); + + await this.typescriptController.waitForTranspileDone(); + + // const typescriptController = this.typescriptController; + + // async function getAllChildModules(filePath: string): Promise { + // const job = await typescriptController.getModuleMap(filePath); + + // const fullModules = [...job.importedModules]; + + // const childModules = job.importedModules ?? []; + + // for (const importedModule of childModules) { + // if (importedModule === null) { + // continue; + // } + + // fullModules.push(...(await getAllChildModules(importedModule))); + // } + + // return [filePath, ...fullModules]; + // } + + // const moduleFiles = await getAllChildModules(jobOutput.filePath); + } + + public async renderHTML(path: string): Promise { + await timeout(10); + + const appHTML = this.ssrController.renderApp(path); + + return ` + + TS-ESWeb + + + +
${appHTML}
+ + + `; + } +} diff --git a/src/Modules/Server/ServerOptions.ts b/src/Modules/Server/ServerOptions.ts new file mode 100644 index 0000000..7f71a4e --- /dev/null +++ b/src/Modules/Server/ServerOptions.ts @@ -0,0 +1,63 @@ +// src/Modules/Server/ServerOptions.ts +import { Expose, Transform, Type } from 'class-transformer'; +import { + IsDefined, + IsEnum, + IsOptional, + IsUUID, + ValidateNested, +} from 'class-validator'; +import Container, { Token } from 'typedi'; +import { Environment, envMode } from '../../Utils/Environment'; +import { RedisOptions } from '../Redis/RedisOptions'; +import { SSROptions } from '../SSR/SSROptions'; +import { + WebAppManifest, + webAppManifestToken, +} from '../WebAppManifest/WebAppManifest'; + +export class ServerOptions { + @IsDefined() + @ValidateNested() + @Type(() => RedisOptions) + public redis: RedisOptions; + + @IsDefined() + @ValidateNested({ + always: true, + }) + @Type(() => SSROptions) + public ssr: SSROptions; + + @IsDefined() + @ValidateNested({ + always: true, + }) + @Type(() => WebAppManifest) + @Transform(({ value, obj }) => { + const container = Container.of((obj as ServerOptions).serverId); + + container.set(webAppManifestToken, value); + + return container.get(webAppManifestToken); + }) + public readonly manifest: WebAppManifest; + + @IsUUID() + @IsDefined() + public serverId: string; + + @Expose() + @IsEnum(Environment) + @IsOptional() + @Transform(({ value }) => { + if (typeof value === 'undefined') { + return envMode; + } + + return value as Environment; + }) + public envMode: Environment; +} + +export const serverOptionsToken = new Token('serverOptions'); diff --git a/src/Modules/Server/createSSRServer.ts b/src/Modules/Server/createSSRServer.ts deleted file mode 100644 index a1c3c6a..0000000 --- a/src/Modules/Server/createSSRServer.ts +++ /dev/null @@ -1,22 +0,0 @@ -// src/Modules/Server/createSSRServer.ts -import { logger } from '../../Library/Logger'; -import { timeout } from '../../Utils/timeout'; -import { ClientOptions, SSRServer } from './SSRServer'; - -interface SSRServerOptions { - options: ClientOptions; -} - -export let ssrServer: SSRServer; - -export async function createSSRServer({ - options, -}: SSRServerOptions): Promise { - await timeout(50); - - logger.info(`Creating SSR Server with specified configuration`); - - ssrServer = new SSRServer(options); - - return ssrServer; -} diff --git a/src/Modules/Server/index.ts b/src/Modules/Server/index.ts deleted file mode 100644 index 0feac66..0000000 --- a/src/Modules/Server/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// src/Modules/Server/index.ts -export { createSSRServer } from './createSSRServer'; diff --git a/src/Modules/ServiceWorker/ServiceWorker.ts b/src/Modules/ServiceWorker/ServiceWorker.ts deleted file mode 100644 index ffea884..0000000 --- a/src/Modules/ServiceWorker/ServiceWorker.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* eslint-disable @typescript-eslint/no-misused-promises */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unused-vars */ -// src/Modules/ServiceWorker/ServiceWorker.ts -// src/Web/serviceWorker.ts -const _self = (self as unknown) as ServiceWorkerGlobalScope; -declare const clients: Clients; - -importScripts( - 'https://storage.googleapis.com/workbox-cdn/releases/5.1.2/workbox-sw.js', -); -if (!workbox) throw new Error('Workbox not definied'); - -interface WebModuleQueryResponse { - filePath: string; - specifier: string; - code: string; -} - -type WebModuleQuery = { data: { webModule: WebModuleQueryResponse | null } }; - -workbox.routing.registerRoute( - /\/Static\/import/, - async ({ url, request, event, params }) => { - const moduleSpecifier = url.searchParams.get('specifier'); - - let response: Response; - if (moduleSpecifier) { - console.log(`specifier: ${moduleSpecifier}`); - const apiHeaders = new Headers(); - apiHeaders.set('content-type', 'application/json'); - - const request = new Request('/graphql', { - body: `{"operationName":"webModule","variables":{"moduleSpecifier":"${moduleSpecifier}"},"query":"query webModule($moduleSpecifier: String!) { webModule(filter: {specifier: $moduleSpecifier}) { filePath specifiers code }}"}`, - method: 'POST', - headers: apiHeaders, - }); - - const fetchResponse = await fetch(request); - const queryResult = (await fetchResponse.json()) as WebModuleQuery; - - if (queryResult.data.webModule) { - response = new Response(queryResult.data.webModule.code); - } else { - throw new Error('What?'); - } - } else { - console.log('Getting file: ', url, request, event, params); - response = new Response(`console.log('helloWorld')`); - } - - response.headers.set('Content-Type', 'application/javascript'); - - return response; - }, -); -const channel = new BroadcastChannel('sw-messages'); - -interface ServiceWorkerSkipWaitingMessage { - type: 'SKIP_WAITING'; -} - -interface ServiceWorkerClientClaimMessage { - type: 'CLIENTS_CLAIM'; -} - -type ServiceWorkerMessage = MessageEvent & { - data: ServiceWorkerSkipWaitingMessage | ServiceWorkerClientClaimMessage; -}; - -interface ServiceWorkerDoneMessage { - type: 'DONE'; - command: 'CLIENTS_CLAIM' | 'SKIP_WAITING'; -} - -self.addEventListener('message', async (event: ServiceWorkerMessage) => { - switch (event.data.type) { - case 'SKIP_WAITING': - await _self.skipWaiting(); - - // self.postMessage({ - // type: 'DONE', - // command: 'SKIP_WAITING', - // } as ServiceWorkerDoneMessage); - break; - case 'CLIENTS_CLAIM': - workbox.core.clientsClaim(); - - await clients.claim(); - channel.postMessage({ type: 'READY' }); - // self.postMessage({ - // type: 'DONE', - // command: 'CLIENTS_CLAIM', - // } as ServiceWorkerDoneMessage); - break; - } -}); - -_self.addEventListener('activate', () => { - console.log('SHIJFKDSHLFKJD'); -}); diff --git a/src/Modules/TypeScript/ModuleMapWorkerJobInput.ts b/src/Modules/TypeScript/ModuleMapWorkerJobInput.ts new file mode 100644 index 0000000..06a1b2a --- /dev/null +++ b/src/Modules/TypeScript/ModuleMapWorkerJobInput.ts @@ -0,0 +1,26 @@ +// src/Modules/TypeScript/ModuleMapWorkerJobInput.ts +import { plainToClass } from 'class-transformer'; +import { IsOptional, IsString, validateOrReject } from 'class-validator'; +import { logger } from '../../Library/Logger'; + +export class ModuleMapWorkerJobInput { + @IsString() + public filePath: string; + + @IsString() + @IsOptional() + public specifier?: string; + + public static async createModuleMapJobInput( + inputParams: Partial, + ): Promise { + logger.silly(`ModuleMapWorkerJobInput.createModuleMapJobInput()`, { + inputParams, + }); + + const jobInput = plainToClass(ModuleMapWorkerJobInput, inputParams); + await validateOrReject(jobInput); + + return jobInput; + } +} diff --git a/src/Modules/TypeScript/Regex.ts b/src/Modules/TypeScript/Regex.ts index 1b2b323..411ffb3 100644 --- a/src/Modules/TypeScript/Regex.ts +++ b/src/Modules/TypeScript/Regex.ts @@ -3,4 +3,6 @@ export const objectExport = /^\{$\n(?((?^\s+)\S+\s+=\s+\{$\n\k export const processNodeReplacement = /if\s+\(process\.env\.NODE_ENV \S+\s"production"\)\s{\n\s+\(function\(\)\s{\n(?(.*\n?)*)}\)\(\);\n}/gim; -export const exportsHandler = /(^exports\.(?\S+)(?=\s+=.*$))(?=.*exports\.\k\s+=)/gmsu; +export const processNodeIfStatement = /if\s*\(process\.env\.NODE_ENV\s*\S+\s('|")production("|')\)\s*\{(?.*)\}\s*else\s*{.*\}/gims; + +export const exportsHandler = /((?<=^|;)exports\.(?\S+)(?=\s*=.*;))(?=.*exports\.\k\s*=)/gmsu; diff --git a/src/Modules/TypeScript/TSConfig.ts b/src/Modules/TypeScript/TSConfig.ts index 54f991a..b760920 100644 --- a/src/Modules/TypeScript/TSConfig.ts +++ b/src/Modules/TypeScript/TSConfig.ts @@ -2,20 +2,19 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/unbound-method */ // src/Modules/TypeScript/TSConfig.ts +import { dirname as pathDirname, isAbsolute as isAbsolutePath } from 'path'; import { CompilerOptions, - ScriptTarget, + convertCompilerOptionsFromJson, + findConfigFile, + JsxEmit, ModuleKind, ModuleResolutionKind, - JsxEmit, - findConfigFile, - sys, - convertCompilerOptionsFromJson, readConfigFile, + ScriptTarget, + sys, } from 'typescript'; -import { isAbsolute as isAbsolutePath, dirname as pathDirname } from 'path'; - let tsConfigCache: CompilerOptions; export function getTSConfig(modulePath: string): CompilerOptions { diff --git a/src/Modules/TypeScript/TSTranspiler.ts b/src/Modules/TypeScript/TSTranspiler.ts deleted file mode 100644 index b3b5d64..0000000 --- a/src/Modules/TypeScript/TSTranspiler.ts +++ /dev/null @@ -1,32 +0,0 @@ -// src/Server/Modules/TypeScript/WebCompiler.ts -import { cpus } from 'os'; -import { logger } from '../../Library/Logger'; -import { WorkerController } from './WorkerController'; - -interface TranspilerOptions { - /** - * Number of worker threads to spin up. - */ - threadCount: number; -} - -/** - * Starts a Web Transpiler starting with the specified entrypoint - * @param entryPath Client Entrypoint to App - */ -export async function startWebTranspiler( - filePath: string, - opts: TranspilerOptions = { - threadCount: cpus().length - 1, - }, -): Promise { - const workerController = await WorkerController.spawnWorkers( - opts.threadCount, - ); - - logger.info( - `startWebTranspiler() Spawned threads. Starting jobs with ${filePath} as the entrypoint`, - ); - - await workerController.start(filePath); -} diff --git a/src/Modules/TypeScript/TranspilerWorkerJobInput.ts b/src/Modules/TypeScript/TranspilerWorkerJobInput.ts new file mode 100644 index 0000000..75dc83b --- /dev/null +++ b/src/Modules/TypeScript/TranspilerWorkerJobInput.ts @@ -0,0 +1,17 @@ +// src/Modules/TypeScript/TranspilerWorkerJobInput.ts +import { plainToClass } from 'class-transformer'; +import { IsString, validateOrReject } from 'class-validator'; + +export class TranspilerWorkerJobInput { + @IsString() + public filePath: string; + + public static async createTranspilerWorkerJobInput( + inputParams: Partial, + ): Promise { + const jobInput = plainToClass(TranspilerWorkerJobInput, inputParams); + await validateOrReject(jobInput); + + return jobInput; + } +} diff --git a/src/Modules/TypeScript/TypeScriptController.ts b/src/Modules/TypeScript/TypeScriptController.ts new file mode 100644 index 0000000..8abe85f --- /dev/null +++ b/src/Modules/TypeScript/TypeScriptController.ts @@ -0,0 +1,133 @@ +// src/Modules/TypeScript/TypeScriptController.ts +import { Job } from 'bullmq'; +import { Inject, Service } from 'typedi'; +import { fileURLToPath } from 'url'; +import { logger } from '../../Library/Logger'; +import { Queue } from '../Queues/Queue'; +import { QueueController } from '../Queues/QueueController'; +import { ServerOptions, serverOptionsToken } from '../Server/ServerOptions'; +import { ModuleMapWorkerJobInput } from './ModuleMapWorkerJobInput'; +import { TranspilerWorkerJobInput } from './TranspilerWorkerJobInput'; + +@Service() +export class TypeScriptController { + /** + * Transpiler Que and Workers for transpiling Files to ESModules + */ + private transpilerQueue: Queue< + 'typescriptTranspiler', + TranspilerWorkerJobInput + >; + + /** + * Module Map Que and Workers for determining all imported and related modules and files + */ + private moduleMapQueue: Queue<'typescriptModuleMap', ModuleMapWorkerJobInput>; + + public constructor( + @Inject(serverOptionsToken) + public options: ServerOptions, + @Inject(() => QueueController) + private queueController: QueueController, + ) { + logger.debug(`TypeScriptController.constructor()`); + + const typescriptTranspilerKey = 'typescriptTranspiler'; + const moduleMapQueKey = 'typescriptModuleMap'; + + this.transpilerQueue = this.queueController.createQueue({ + name: typescriptTranspilerKey, + inputClass: TranspilerWorkerJobInput, + }); + + this.moduleMapQueue = this.queueController.createQueue({ + name: moduleMapQueKey, + inputClass: ModuleMapWorkerJobInput, + }); + } + + /** + * Spawn the Module Map Workers + */ + public async createModuleMapWorkers(): Promise { + logger.info(`TypeScriptController.createModuleMapWorkers()`); + + const workerPathURI = await import.meta.resolve( + './TypeScriptModuleMapWorker', + ); + const workerPath = fileURLToPath(workerPathURI); + + /** + * Create the @K-FOSS/TS-ESWorkers. + */ + await this.moduleMapQueue.createWorkers(workerPath, 2); + + logger.debug( + `TypeScriptController.createModuleMapWorkers() workerPathURI: ${workerPathURI}`, + ); + } + + /** + * Spawn the TypeScript Transpiler Workers + */ + public async createTranspilerWorkers(): Promise { + logger.debug(`TypeScriptController.createTranspilerWorkers()`); + + const workerPathURI = await import.meta.resolve( + './TypeScriptTranspilerWorker', + ); + const workerPath = fileURLToPath(workerPathURI); + + await this.transpilerQueue.createWorkers(workerPath, 2); + + logger.silly( + `TypeScriptController.createTranspilerWorkers() workerPathURI: ${workerPathURI}`, + ); + } + + /** + * Transpile a new FilePath + * + * @param jobInput Input to pass to the Transpiler Worker + * + * @returns Promise resolving to the created BullMQ Job + */ + public createTranspilerTask( + jobInput: TranspilerWorkerJobInput, + ): Promise { + logger.silly(`TypeScriptController.createTranspilerTask()`, { + jobInput, + }); + + const job = this.transpilerQueue.addTask(jobInput, { + jobId: jobInput.filePath, + }); + + logger.info(`TypeScriptController.createTask()`); + + return job; + } + + /** + * Create a new "job" to create a module map for a specified input module + * + * @param jobInput Input to pass to the Module Map Worker + * + * @returns Promise resolving to the created BullMQ Job + */ + public async createModuleMapTask( + jobInput: ModuleMapWorkerJobInput, + ): Promise { + return this.moduleMapQueue.addTask(jobInput, { + jobId: jobInput.filePath, + }); + } + + public waitForModuleMapDone(): Promise { + return this.moduleMapQueue.waitForDone(); + } + + public waitForTranspileDone(): Promise { + return this.transpilerQueue.waitForDone(); + } +} diff --git a/src/Modules/TypeScript/TypeScriptModuleMapWorker.ts b/src/Modules/TypeScript/TypeScriptModuleMapWorker.ts new file mode 100644 index 0000000..859897e --- /dev/null +++ b/src/Modules/TypeScript/TypeScriptModuleMapWorker.ts @@ -0,0 +1,365 @@ +// src/Modules/TypeScript/TypeScriptModuleMapWorker.ts +import { Queue, Worker } from 'bullmq'; +import { isString } from 'class-validator'; +import { readFile } from 'fs/promises'; +import { resolve } from 'node:path'; +import { dirname } from 'path'; +import Container from 'typedi'; +import * as ts from 'typescript'; +import { fileURLToPath, pathToFileURL } from 'url'; +import { threadId } from 'worker_threads'; +import { logger as coreLogger } from '../../Library/Logger'; +import { QueueController } from '../Queues/QueueController'; +import { queueToken } from '../Queues/QueueToken'; +import { workerControllerToken } from '../Queues/WorkerController'; +import { workerInputToken } from '../Queues/WorkerInput'; +import { WebModuleController } from '../WebModule/WebModuleController'; +import { ModuleMapWorkerJobInput } from './ModuleMapWorkerJobInput'; +import { TranspilerWorkerJobInput } from './TranspilerWorkerJobInput'; +import { createTypeScriptProgram, isCommonJSImportSplit } from './Utils'; + +const logger = coreLogger.child({ + labels: { worker: 'TypeScriptModuleMapWorker.ts', workerId: threadId }, +}); + +logger.info(`Worker starting`, { + labels: { + worker: 'TypeScriptModuleMapWorker.ts', + workerId: threadId, + }, +}); + +const queueController = Container.get(QueueController); + +const workerController = Container.get(workerControllerToken); + +const queueName = Container.get(queueToken); +const workerInput = Container.get(workerInputToken); + +workerController.logger.silly(`workerInput`, { + queueController, + queueName, +}); + +const webModuleController = Container.get(WebModuleController); + +const transpilerQue = new Queue('typescriptTranspiler', { + ...workerInput.queueOptions, +}); + +const moduleMapQue = new Queue(queueName, { + ...workerInput.queueOptions, +}); + +/** + * Discover all imported modules and add to the TypeScript Module Map + * @param moduleInput Input Params + * @returns Promise resolving to void once completed + */ +async function discoverModuleMap( + moduleInput: ModuleMapWorkerJobInput, +): Promise { + const moduleMapLogger = workerController.logger.child({ + labels: { + filePath: moduleInput.filePath, + worker: 'TypeScriptModuleMapWorker.ts', + workerId: threadId, + appName: 'TS-ESWeb', + }, + }); + + moduleMapLogger.debug(`discoverModuleMap()`, { + params: moduleInput, + }); + + const rootDir = dirname(moduleInput.filePath); + + const compilerOptions: ts.CompilerOptions = { + jsxFragmentFactory: 'Fragment', + target: ts.ScriptTarget.ESNext, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.NodeJs, + allowJs: true, + checkJs: false, + noEmit: true, + noEmitHelpers: true, + noLib: true, + isolatedModules: true, + skipLibCheck: true, + skipDefaultLibCheck: true, + sourceMap: false, + inlineSourceMap: false, + }; + + const compilerProgram = await createTypeScriptProgram({ + rootDir, + rootNames: [moduleInput.filePath], + compilerOptions, + }); + + moduleMapLogger.silly(`compilerProgram created`, { + rootDir, + rootNames: [moduleInput.filePath], + compilerOptions, + }); + if ( + moduleInput.filePath === + '/workspace/node_modules/@babel/runtime/helpers/extends/_index.mjs' + ) { + const transpilerJobInput = await TranspilerWorkerJobInput.createTranspilerWorkerJobInput( + { + filePath: moduleInput.filePath, + }, + ); + + await transpilerQue.add('typescriptTranspiler', transpilerJobInput, { + jobId: transpilerJobInput.filePath, + }); + + await webModuleController.addWebModuleMapJob({ + ...moduleInput, + importedModules: [], + }); + + // await redisController.IORedis.set( + // redisController.getRedisKey(RedisType.MODULE_MAP, moduleInput.filePath), + // JSON.stringify(webModuleJobInputParams), + // ); + + // await redisController.IORedis.set( + // redisController.getRedisKey(RedisType.MODULE_MAP, specifier), + // JSON.stringify(webModuleJobInputParams), + // ); + + return; + } + + const sourceFile = compilerProgram.getSourceFile(moduleInput.filePath); + + if (!sourceFile) { + throw new Error('Invalid Source File'); + } + + moduleMapLogger.silly(`sourceFile: `, { + objectName: 'sourceFile', + }); + + const resolvedArray = Array.from( + sourceFile.resolvedModules ?? new Map([]), + ); + + moduleMapLogger.silly(`resolvedArray`, { + resolvedArray, + }); + + if (isCommonJSImportSplit(resolvedArray) && moduleInput.specifier) { + moduleMapLogger.silly(`Module has two resolvedModules`, { + resolvedArray, + moduleInput, + specifier: moduleInput.specifier, + }); + + try { + const [, outputModule] = resolvedArray.reduce( + (_test, [_filePath, resolvedModule]) => { + workerController.logger.silly(`resolvedArrayReduce`, { + _filePath, + resolvedModule, + includes: workerInput.serverOptions.envMode, + _test, + specifier: moduleInput.specifier, + }); + + if ( + resolvedModule.packageId?.subModuleName.includes( + workerInput.serverOptions.envMode, + ) + ) { + return [_filePath, resolvedModule]; + } else { + return _test; + } + }, + ); + + const jobData = await ModuleMapWorkerJobInput.createModuleMapJobInput({ + filePath: outputModule.resolvedFileName, + specifier: moduleInput.specifier, + }); + + moduleMapLogger.silly('Output real module: ', { + outputModule, + jobData, + specifier: moduleInput.specifier, + }); + + await moduleMapQue.add(workerInput.queName, jobData, { + jobId: outputModule.resolvedFileName, + }); + + return; + } catch (err) { + moduleMapLogger.error('Error during reduce', { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + err, + specifier: moduleInput.specifier, + }); + } + } + + const importedModules = await Promise.all( + resolvedArray.map(async ([specifier, _test]) => { + const parentURI = pathToFileURL(moduleInput.filePath); + + moduleMapLogger.silly(`Resolving Module`, { + specifier, + parentURI, + _test, + }); + + const resolvePathURI = await import.meta.resolve( + specifier, + parentURI.href, + ); + + if (resolvePathURI.startsWith('node:')) { + return; + } + + moduleMapLogger.silly(`Resolved moduleURI`, { + specifier, + parentURI, + resolvePathURI, + }); + + let filePath = fileURLToPath(resolvePathURI); + + const folderPath = dirname(filePath); + + let packageJSON: { module?: string }; + + try { + const file = await readFile(resolve(folderPath, 'package.json')); + logger.silly('Package file', { + file: file.toString(), + }); + + packageJSON = JSON.parse(file.toString()) as { module?: string }; + } catch (err) { + packageJSON = {}; + } + + if (isString(packageJSON?.module)) { + logger.silly(`Testing HelloWorld`, { + moduleName: packageJSON.module, + filePath: pathToFileURL(folderPath).href, + }); + + filePath = resolve(folderPath, packageJSON.module); + } + + let moduleSpecifier: string; + if (ts.isExternalModuleNameRelative(specifier)) { + logger.silly('Module is external relative', { + specifier, + filePath, + _test, + newSpecifier: _test.packageId?.name + ? `${_test.packageId.name}/${_test.packageId.subModuleName}` + : filePath, + }); + moduleSpecifier = _test.packageId?.name + ? `${_test.packageId.name}/${_test.packageId.subModuleName}` + : filePath; + } else { + moduleSpecifier = specifier; + } + + moduleMapLogger.silly(`Adding module to Que`, { + specifier, + parentURI, + resolvePathURI, + }); + + const jobData = await ModuleMapWorkerJobInput.createModuleMapJobInput({ + filePath, + specifier: moduleSpecifier, + }); + + moduleMapLogger.silly(`newJobData: `, { + jobData, + jobId: filePath, + }); + + /** + * TODO: Add dep to moduleMap + */ + + const job = await moduleMapQue.add(workerInput.queName, jobData, { + jobId: filePath, + }); + + return job.id; + }), + ); + + await webModuleController.addWebModuleMapJob({ + ...moduleInput, + importedModules: importedModules.filter(Boolean) as string[], + }); + + const transpilerJobInput = await TranspilerWorkerJobInput.createTranspilerWorkerJobInput( + { + filePath: moduleInput.filePath, + }, + ); + + workerController.logger.silly(`Creating Transpiler Task`, { + jobId: transpilerJobInput.filePath, + transpilerJobInput, + }); + + await transpilerQue.add('typescriptTranspiler', transpilerJobInput, { + jobId: transpilerJobInput.filePath, + }); +} + +const moduleWorkerLogger = workerController.logger.child({ + worker: 'moduleWorker', + labels: { + appName: 'TS-ESWeb', + worker: 'moduleWorker', + }, +}); + +const moduleWorker = new Worker( + workerInput.queName, + async (job) => { + if (moduleWorkerLogger.isSillyEnabled()) { + moduleWorkerLogger.silly(`Task recieved`, { + jobInput: job.data, + }); + } else { + moduleWorkerLogger.info(`Recieved a task for moduleWorker`); + } + + const jobInput = await ModuleMapWorkerJobInput.createModuleMapJobInput( + job.data, + ); + + moduleWorkerLogger.silly(`Transformed job.data to Class`, { + objectName: 'jobInput', + jobInput, + }); + + await discoverModuleMap(jobInput); + }, + { + connection: workerInput.queueOptions.connection, + }, +); + +workerController.logger.silly(`Created moduleWorker`, { + objectName: 'moduleWorker.name', + value: moduleWorker.name, +}); diff --git a/src/Modules/TypeScript/TypeScriptTranspilerWorker.ts b/src/Modules/TypeScript/TypeScriptTranspilerWorker.ts new file mode 100644 index 0000000..a6e3de2 --- /dev/null +++ b/src/Modules/TypeScript/TypeScriptTranspilerWorker.ts @@ -0,0 +1,153 @@ +// src/Modules/TypeScript/TypeScriptTranspilerWorker.ts +import { Worker } from 'bullmq'; +import { plainToClass } from 'class-transformer'; +import { validateOrReject } from 'class-validator'; +import { readFile } from 'fs/promises'; +import Container from 'typedi'; +import { + JsxEmit, + ModuleKind, + ModuleResolutionKind, + ScriptTarget, + transpileModule, +} from 'typescript'; +import { threadId } from 'worker_threads'; +import { logger as coreLogger } from '../../Library/Logger'; +import { TypeScriptTransformerController } from '../../Library/Transformers'; +import { Environment } from '../../Utils/Environment'; +import { processModule } from '../Files/processModule'; +import { queueToken } from '../Queues/QueueToken'; +import { workerControllerToken } from '../Queues/WorkerController'; +import { workerInputToken } from '../Queues/WorkerInput'; +import { RedisController } from '../Redis/RedisController'; +import { RedisType } from '../Redis/RedisTypes'; +import { WebModule } from '../WebModule'; +import { WebModuleController } from '../WebModule/WebModuleController'; +import { TranspilerWorkerJobInput } from './TranspilerWorkerJobInput'; + +const logger = coreLogger.child({ + labels: { worker: 'TypeScriptTranspilerWorker.ts', workeId: threadId }, +}); + +logger.info(`Worker starting`, { + labels: { + worker: 'TypeScriptTranspilerWorker.ts', + workeId: threadId, + }, +}); + +// const queueController = Container.get(QueueController); + +const workerController = Container.get(workerControllerToken); + +const webModuleController = Container.get(WebModuleController); + +const queueName = Container.get(queueToken); +const workerInput = Container.get(workerInputToken); + +const redisController = Container.get(RedisController); + +logger.debug(`Retrieved workerData:`, { + objectName: 'workerInput', + workerInput, +}); + +const transformerController = new TypeScriptTransformerController(); + +/** + * Transform provided filepath to ESM with TypeScript Compiler API + * @param filePath Path to file to load, transform, and transpile to ESM + * @returns Promise resolving to string of tranformed file. + */ +async function transformFile(filePath: string): Promise { + workerController.logger.info(`Transforming ${filePath}`); + + workerController.logger.silly(`Loading File`, { + filePath, + }); + + const file = await readFile(filePath); + + workerController.logger.silly(`Getting TypeScript Transformers`); + + const transformers = await transformerController.loadTransformers(); + + workerController.logger.silly(`Transformers`, { + transformers, + }); + + const transpiledModule = transpileModule(processModule(file.toString()), { + fileName: filePath, + transformers, + compilerOptions: { + allowJs: true, + checkJs: false, + module: ModuleKind.ESNext, + moduleResolution: ModuleResolutionKind.NodeJs, + target: ScriptTarget.ESNext, + inlineSourceMap: true, + jsx: + workerInput.serverOptions.envMode === Environment.PRODUCTION + ? JsxEmit.ReactJSX + : JsxEmit.ReactJSXDev, + jsxFragmentFactory: 'Fragment', + }, + }); + + return transpiledModule.outputText.replaceAll('exports.', ''); +} + +const transpilerWorker = new Worker( + queueName, + async (job) => { + logger.info(`Recieved a task for transpilerWorker`, { + worker: 'transpilerWorker', + }); + + logger.debug(`Task input`, { + worker: 'transpilerWorker', + jobInput: job.data, + }); + + const jobInput = plainToClass(TranspilerWorkerJobInput, job.data); + + logger.debug(`Transformed job.data to Class`, { + worker: 'transpilerWorker', + objectName: 'jobInput', + jobInput, + }); + + await validateOrReject(jobInput); + + logger.debug(`Validated jobInput`, { + worker: 'transpilerWorker', + jobInput, + }); + + const sourceText = await transformFile(jobInput.filePath); + + logger.silly(`Module ${jobInput.filePath} has been transpiled`); + + await redisController.setValue(RedisType.MODULE, { + key: jobInput.filePath, + value: sourceText, + }); + + const webModule = await WebModule.createWebModule({ + filePath: jobInput.filePath, + }); + await webModuleController.setWebModule(webModule); + + logger.silly('Done', { + filePath: jobInput.filePath, + }); + }, + { + connection: workerInput.queueOptions.connection, + }, +); + +logger.silly(`Created transpilerWorker`, { + objectName: 'transpilerWorker', + transpilerWorkerName: transpilerWorker.name, +}); diff --git a/src/Modules/TypeScript/Utils.test.ts b/src/Modules/TypeScript/Utils.test.ts new file mode 100644 index 0000000..d1e3cb9 --- /dev/null +++ b/src/Modules/TypeScript/Utils.test.ts @@ -0,0 +1,59 @@ +// src/Modules/TypeScript/Utils.test.ts +import { TestSuite } from '@k-foss/ts-estests'; +import { dirname } from 'path'; +import * as ts from 'typescript'; +import { fileURLToPath } from 'url'; +import { createTypeScriptProgram } from './Utils'; + +export class TypeScriptUtilsSuite extends TestSuite { + public testName = 'TypeScript Utils Test Suite'; + + public async relativeFile(): Promise { + const filePathURI = await import.meta.resolve('./TSConfig.ts'); + const filePath = fileURLToPath(filePathURI); + + await createTypeScriptProgram({ + rootNames: [], + rootDir: filePath, + compilerOptions: { + jsxFragmentFactory: 'Fragment', + target: ts.ScriptTarget.ESNext, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.NodeJs, + allowJs: true, + checkJs: false, + noEmit: true, + noEmitHelpers: true, + sourceMap: false, + inlineSourceMap: false, + }, + }); + } + + public async relativePath(): Promise { + const filePathURI = await import.meta.resolve('./TSConfig.ts'); + const filePath = fileURLToPath(filePathURI); + const rootDir = dirname(filePath); + + await createTypeScriptProgram({ + rootNames: [], + rootDir, + compilerOptions: { + jsxFragmentFactory: 'Fragment', + target: ts.ScriptTarget.ESNext, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.NodeJs, + allowJs: true, + checkJs: false, + noEmit: true, + noEmitHelpers: true, + sourceMap: false, + inlineSourceMap: false, + }, + }); + } + + public async test(): Promise { + await Promise.all([this.relativePath(), this.relativeFile()]); + } +} diff --git a/src/Modules/TypeScript/Utils.ts b/src/Modules/TypeScript/Utils.ts new file mode 100644 index 0000000..7658e5c --- /dev/null +++ b/src/Modules/TypeScript/Utils.ts @@ -0,0 +1,96 @@ +// src/Modules/TypeScript/Utils.ts +import { plainToClass, Transform } from 'class-transformer'; +import { + IsObject, + isString, + IsString, + validateOrReject, +} from 'class-validator'; +import { parse } from 'path'; +import * as ts from 'typescript'; +import { logger } from '../../Library/Logger'; + +class CreateTypeScriptProgramInput { + @IsString() + @Transform(({ value }) => { + if (isString(value)) { + const parsedPath = parse(value); + + if (parsedPath.ext.length > 0) { + return parsedPath.dir; + } else { + return value; + } + } + + throw new Error('Invalid string'); + }) + public rootDir: string; + + @IsString({ + each: true, + }) + public rootNames: string[]; + + @IsObject() + public compilerOptions: ts.CompilerOptions; +} + +export async function createTypeScriptProgram( + inputParams: CreateTypeScriptProgramInput, +): Promise { + const input = plainToClass(CreateTypeScriptProgramInput, inputParams); + await validateOrReject(input); + + const defaultOptions = ts.getDefaultCompilerOptions(); + const options: ts.CompilerOptions = { + ...defaultOptions, + ...inputParams.compilerOptions, + }; + + logger.silly(`createTypeScriptProgram`, { + inputParams, + input, + options, + }); + + const compilierHost = ts.createCompilerHost({ + ...options, + rootDir: inputParams.rootDir, + }); + + return ts.createProgram({ + rootNames: inputParams.rootNames, + options, + host: compilierHost, + }); +} + +type NonPartial = { [P in keyof T]: T[P] }; + +export function isCommonJSImportSplit( + input: [string, ts.ResolvedModuleFull][], +): input is [ + [string, NonPartial], + [string, NonPartial], +] { + if ( + input.length === 2 && + input.every(([_path, resolvedModule]) => + resolvedModule.packageId?.subModuleName.includes('cjs'), + ) + ) { + return true; + } + + if ( + input.length === 2 && + input.every(([_path, resolvedModule]) => + resolvedModule.packageId?.subModuleName.includes('umd'), + ) + ) { + return true; + } + + return false; +} diff --git a/src/Modules/TypeScript/WorkerController.ts b/src/Modules/TypeScript/WorkerController.ts deleted file mode 100644 index edb4330..0000000 --- a/src/Modules/TypeScript/WorkerController.ts +++ /dev/null @@ -1,211 +0,0 @@ -// src/Server/Modules/TypeScript/WorkerController.ts -import { spawnWorker } from '@k-foss/ts-worker'; -import { fileURLToPath } from 'url'; -import { Worker } from 'worker_threads'; -import { logger } from '../../Library/Logger'; -import { BaseEventEmitter } from '../../Utils/Events'; -import { HMR } from '../HMR'; -import { WebModule } from '../WebModule'; -import { webModuleController } from '../WebModule/WebModuleController'; -import { - TranspileWorkerMessage, - TranspileWorkerMessageType, -} from './WorkerMessages'; - -interface WorkerControllerEventMap { - fileTranspiled: unknown; - done: boolean; -} - -interface WorkerThread { - workerThread: Worker; - - ready: boolean; - - online: boolean; -} - -interface SpawnWorkersOptions { - cache: boolean; -} - -export class WorkerController extends BaseEventEmitter { - public workers: WorkerThread[] = []; - public started = false; - public cache = true; - - public get threads(): number { - return this.workers.length; - } - - public get lazyWorkers(): WorkerThread[] { - return this.workers.filter( - ({ ready, online }) => ready === true && online === true, - ); - } - - public get lazyThreads(): number { - return this.lazyWorkers.length; - } - - private pathHistory = new Set(); - private jobQue = new Set(); - - public static async spawnWorkers( - threadCount: number, - { cache } = { cache: true } as SpawnWorkersOptions, - ): Promise { - const controller = new WorkerController(); - controller.cache = cache; - - const workerModulePath = await import.meta.resolve( - './transpileWorker', - import.meta.url, - ); - - for (const _workerThread of Array(threadCount).fill(0)) { - const worker = spawnWorker(fileURLToPath(workerModulePath), {}); - controller.workers.push({ - workerThread: worker, - online: false, - ready: false, - }); - - worker.on('online', () => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const workerThread = controller.workers.find( - ({ workerThread }) => workerThread.threadId === worker.threadId, - )!; - workerThread.ready = true; - workerThread.online = true; - }); - - worker.on('message', controller.handleWorkerMessage(worker)); - - controller.on('done', () => { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - if (controller.cache) worker.terminate(); - }); - } - - return controller; - } - - public addJob(filePath: string): void { - if (this.pathHistory.has(filePath)) { - return; - } else if (this.jobQue.has(filePath)) { - return; - } else if (webModuleController.getModule(filePath)) { - return; - } - - this.jobQue.add(filePath); - this.pathHistory.add(filePath); - } - - public handleWorkerMessage( - worker: Worker, - ): (msg: TranspileWorkerMessage) => void { - return (msg): void => { - switch (msg.type) { - case TranspileWorkerMessageType.PUSH_DEPENDENCY: - if (this.started === false) this.started = true; - - logger.debug( - `WorkerController handleWorkerMessage(): added ${msg.filePath}`, - ); - - this.addJob(msg.filePath); - - break; - case TranspileWorkerMessageType.PUSH_HMR: - HMR.watchedFiles.add(msg.filePath); - - break; - case TranspileWorkerMessageType.PUSH_OUTPUT: - webModuleController.pushModule( - msg.filePath, - new WebModule({ - code: msg.outputCode, - dependencies: new Set(), - filePath: msg.filePath, - }), - ); - - break; - case TranspileWorkerMessageType.READY: - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.workers.find( - ({ workerThread }) => workerThread.threadId === worker.threadId, - )!.ready = true; - - break; - } - }; - } - - public removeJob(filePath: string): void { - this.jobQue.delete(filePath); - } - - public forceAddJob(filePath: string): Promise { - this.jobQue.add(filePath); - - return new Promise((resolve) => { - this.on('done', resolve); - }); - } - - public startPolling(): void { - webModuleController.on('newModule', (msg) => { - if (this.jobQue.has(msg.filePath)) this.removeJob(msg.filePath); - }); - - const poll = setInterval(() => { - const jobQueArray = Array.from(this.jobQue); - - if (this.lazyThreads > 0 && jobQueArray.length > 0) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const lazyWorker = this.lazyWorkers.pop()!; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const filePath = jobQueArray.pop()!; - - if (webModuleController.getModule(filePath) && this.cache) { - this.removeJob(filePath); - return; - } - - this.removeJob(filePath); - lazyWorker.workerThread.postMessage(filePath); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.workers.find( - ({ workerThread }) => - workerThread.threadId === lazyWorker.workerThread.threadId, - )!.ready = false; - } - - if (this.lazyThreads === this.threads && this.started === true) { - this.emit('done', true); - if (this.cache) { - clearInterval(poll); - } - } - }, 500); - } - - /** - * Starts transpiling a module graph starting with the entry - * @param entrypoint Path to entryfile - */ - public async start(filePath: string): Promise { - this.addJob(filePath); - - this.startPolling(); - - return new Promise((resolve) => { - if (this.cache) this.on('done', resolve); - }); - } -} diff --git a/src/Modules/TypeScript/WorkerMessages.ts b/src/Modules/TypeScript/WorkerMessages.ts deleted file mode 100644 index 0890b0c..0000000 --- a/src/Modules/TypeScript/WorkerMessages.ts +++ /dev/null @@ -1,39 +0,0 @@ -// src/Modules/TypeScript/WorkerMessages.ts -export enum TranspileWorkerMessageType { - READY, - PUSH_OUTPUT, - PUSH_DEPENDENCY, - PUSH_HMR, -} - -interface TranspileWorkerPushOutputMessage { - type: TranspileWorkerMessageType.PUSH_OUTPUT; - - filePath: string; - - outputCode: string; -} - -interface TranspileWorkerPushDependencyMessage { - type: TranspileWorkerMessageType.PUSH_DEPENDENCY; - - specifier: string; - - filePath: string; -} - -interface TranspileWorkerPushHMRMessage { - type: TranspileWorkerMessageType.PUSH_HMR; - - filePath: string; -} - -interface TranspileWorkerReadyMessage { - type: TranspileWorkerMessageType.READY; -} - -export type TranspileWorkerMessage = - | TranspileWorkerPushOutputMessage - | TranspileWorkerReadyMessage - | TranspileWorkerPushHMRMessage - | TranspileWorkerPushDependencyMessage; diff --git a/src/Modules/TypeScript/index.ts b/src/Modules/TypeScript/index.ts deleted file mode 100644 index c4d3586..0000000 --- a/src/Modules/TypeScript/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// src/Modules/TypeScript/index.ts -export { startWebTranspiler } from './TSTranspiler'; diff --git a/src/Modules/TypeScript/transpileWorker.ts b/src/Modules/TypeScript/transpileWorker.ts deleted file mode 100644 index 3e8df87..0000000 --- a/src/Modules/TypeScript/transpileWorker.ts +++ /dev/null @@ -1,160 +0,0 @@ -// src/Server/Utils/Workers/transpileWorker.ts -import { readFileSync } from 'fs'; -import { dirname } from 'path'; -import ts from 'typescript'; -import { fileURLToPath, pathToFileURL } from 'url'; -import { parentPort } from 'worker_threads'; -import { logger } from '../../Library/Logger'; -import { getTransformers } from '../../Library/Transformers'; -import { exportsHandler, objectExport, processNodeReplacement } from './Regex'; -import { getTSConfig } from './TSConfig'; -import { - TranspileWorkerMessage, - TranspileWorkerMessageType, -} from './WorkerMessages'; - -if (parentPort === null) { - throw new Error(`Worker does not have parentPort open`); -} - -/** - * Send the Worker ready for new file message to the Worker Controller - */ -function sendReady(): void { - if (parentPort === null) { - throw new Error(`Worker does not have parentPort open`); - } - - return parentPort.postMessage({ - type: TranspileWorkerMessageType.READY, - } as TranspileWorkerMessage); -} - -/** - * Transpile filePath to brower compatible code - * @param filePath Path to file to Transpile - */ -async function transpilePath(filePath: string): Promise { - const rootDir = dirname(filePath); - - const tsConfig = getTSConfig(filePath); - const defaultOptions = ts.getDefaultCompilerOptions(); - const options = { ...defaultOptions, ...tsConfig }; - - const compilierHost = ts.createCompilerHost({ - ...options, - rootDir, - }); - - const webModulePromises: Promise[] = []; - - compilierHost.readFile = (fileName: string): string => { - const fileContents = readFileSync(fileName); - - const exportVars: string[] = []; - - /** - * https://regex101.com/r/uwAq1N/1 - */ - - let moduleContents = fileContents - ?.toString() - .replace(processNodeReplacement, '$') - .replaceAll(exportsHandler, (test, todo, varName) => { - exportVars.push(varName); - - return `var ${varName}`; - }) - .replace(objectExport, (...args) => { - const { coreCode } = args[args.length - 1]; - - return coreCode.replaceAll('exports.', ''); - }); - - exportVars.map( - (exportVar) => - (moduleContents += `exports.${exportVar} = ${exportVar}\n`), - ); - - return moduleContents; - }; - - /** - * Overwrite the TypeScript write file function so we can intercept the module data - */ - compilierHost.writeFile = ( - _filePath, - contents, - _writeByteOrderMark, - _onError, - sourceFiles, - ): void => { - if (sourceFiles) { - if (!sourceFiles[0]) { - throw new Error('No inital source file. Issue with TS Compile'); - } - - webModulePromises.push( - ...sourceFiles.map(async (sourceFile) => { - parentPort?.postMessage({ - type: TranspileWorkerMessageType.PUSH_OUTPUT, - filePath: sourceFile.fileName, - outputCode: contents.replaceAll('exports.', ''), - } as TranspileWorkerMessage); - - if (sourceFile.resolvedModules) { - for (const [moduleName] of sourceFile.resolvedModules) { - const moduleURLPath = await import.meta.resolve( - moduleName, - pathToFileURL(sourceFile.fileName).href, - ); - - logger.debug( - `transpilerWorker.ts resolvedModuleURL: ${moduleURLPath}`, - ); - - if (moduleURLPath.startsWith('node:')) { - continue; - } - const modulePath = fileURLToPath(moduleURLPath); - - parentPort?.postMessage({ - type: TranspileWorkerMessageType.PUSH_DEPENDENCY, - filePath: modulePath, - specifier: moduleName, - } as TranspileWorkerMessage); - } - } - }), - ); - } - }; - - const compilerProgram = ts.createProgram({ - rootNames: [filePath], - options: { - ...options, - jsxFragmentFactory: 'Fragment', - }, - host: compilierHost, - }); - compilerProgram.emit( - undefined, - undefined, - undefined, - undefined, - await getTransformers(compilerProgram), - ); - - return Promise.all(webModulePromises); -} - -parentPort.on('message', (filePath: string) => { - transpilePath(filePath) - .then(sendReady) - .catch((err) => { - console.error('Transpile has errorer', err); - }); -}); - -sendReady(); diff --git a/src/Modules/WebAppManifest/WebAppManifest.test.ts b/src/Modules/WebAppManifest/WebAppManifest.test.ts index b1f1c95..413bfaa 100644 --- a/src/Modules/WebAppManifest/WebAppManifest.test.ts +++ b/src/Modules/WebAppManifest/WebAppManifest.test.ts @@ -1,9 +1,9 @@ // src/Modules/WebAppManifest/WebAppManifest.test.ts import { TestSuite } from '@k-foss/ts-estests'; import { deepStrictEqual } from 'assert'; -import { classToPlainFromExist, plainToClass } from 'class-transformer'; +import { plainToClass } from 'class-transformer'; import { createFastifyTestServer } from '../../Library/Fastify'; -import { logger } from '../../Library/Logger'; +// import { logger } from '../../Library/Logger'; import '../../Utils/Setup'; import { WebAppManifest } from './WebAppManifest'; import { WebAppManfiestController } from './WebAppManifestController'; @@ -33,7 +33,7 @@ export class WebAppManifestTestSuite extends TestSuite { const webServer = await createFastifyTestServer(); - console.log('Created test server?'); + // console.log('Created test server?'); const manifestRequest = await webServer.inject({ path: '/WebManifest.json', @@ -46,7 +46,7 @@ export class WebAppManifestTestSuite extends TestSuite { `GET Request to /WebManifest returns status code 200 with valid loaded Manifest`, ); - logger.debug(classToPlainFromExist(manifest, manifestRequest)); + // logger.debug(classToPlainFromExist(manifest, manifestRequest)); deepStrictEqual( plainToClass(WebAppManifest, manifestRequest.json()), diff --git a/src/Modules/WebModule/EnvIfStatementTransformer.ts b/src/Modules/WebModule/EnvIfStatementTransformer.ts new file mode 100644 index 0000000..9515bfd --- /dev/null +++ b/src/Modules/WebModule/EnvIfStatementTransformer.ts @@ -0,0 +1,26 @@ +// src/Modules/WebModule/ImportTransformer +import * as ts from 'typescript'; +import { logger } from '../../Library/Logger'; +import { Transformer } from '../../Library/Transformers'; + +export class EnvIfStatement extends Transformer { + public after: Transformer['after'] = (context) => { + // const relativePathRegex = /^\.{1,2}[/]/; + + return (sourceFile: ts.SourceFile): ts.SourceFile => { + const visitor: ts.Visitor = (node) => { + /** + * Handle "Dynamic imports" + */ + + if (ts.isIfStatement(node)) { + logger.silly(`If statement`); + } + + return ts.visitEachChild(node, visitor, context); + }; + + return ts.visitNode(sourceFile, visitor); + }; + }; +} diff --git a/src/Modules/WebModule/ImportTransformer.ts b/src/Modules/WebModule/ImportTransformer.ts index f91d3d2..1061f97 100644 --- a/src/Modules/WebModule/ImportTransformer.ts +++ b/src/Modules/WebModule/ImportTransformer.ts @@ -6,29 +6,65 @@ import { Transformer } from '../../Library/Transformers'; const pushDefault = ['react', 'react-dom', 'scheduler', 'scheduler/tracing']; +/** + * TODO: Setup a type check/`node is XYZ` + * @param node SourceFile Node to test if it's a dynamic import + */ +function isDynamicImport(node: ts.Node): node is ts.ImportCall { + if (ts.isCallExpression(node)) { + return node.expression.kind === ts.SyntaxKind.ImportKeyword; + } + + return false; +} + +function handleImportSpecifier( + sourceSpecifier: string, + sourceDirectory: string, +): string { + let specifier: string; + + const relativeTest = /^\.{0,2}[/]/gm; + + if (relativeTest.test(sourceSpecifier)) { + specifier = resolve(sourceDirectory, sourceSpecifier); + } else { + specifier = sourceSpecifier; + } + + return `/Static/import?specifier=${specifier}`; +} + export class ImportTransformer extends Transformer { - public after: Transformer['before'] = (context) => { + public after: Transformer['after'] = (context) => { // const relativePathRegex = /^\.{1,2}[/]/; return (sourceFile: ts.SourceFile): ts.SourceFile => { const visitor: ts.Visitor = (node) => { - if ( - ts.isCallExpression(node) && - node.expression.kind === ts.SyntaxKind.ImportKeyword - ) { + /** + * Handle "Dynamic imports" + */ + + if (isDynamicImport(node)) { const argument = node.arguments[0]; if (ts.isStringLiteral(argument)) { + logger.silly(`ImportTransformer isCallExpression:`, { + argument: argument.text, + dirName: dirname(sourceFile.fileName), + fileName: sourceFile.fileName, + }); + return context.factory.updateCallExpression( node, node.expression, node.typeArguments, [ context.factory.createStringLiteral( - `/Static/import?specifier=${resolve( - dirname(sourceFile.fileName), + handleImportSpecifier( argument.text, - )}`, + dirname(sourceFile.fileName), + ), ), ], ); @@ -39,39 +75,10 @@ export class ImportTransformer extends Transformer { ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) ) { - // let specifier: string; - // if (relativePathRegex.test(node.moduleSpecifier.text)) { - // specifier = resolvePath( - // node.moduleSpecifier.text, - // pathToFileURL(sourceFile.fileName).href, - // ); - // return node; - // } else { - // specifier = - // console.log( - // `Set new specififer to '/Static/import?specifier=${node.moduleSpecifier.text}'`, - // ); - // } - const relativeTest = /^\.{0,2}[/]/gm; - logger.debug( `ImportTransformer Import declaration: ${node.moduleSpecifier.text}`, ); - let specifier: string; - if (relativeTest.test(node.moduleSpecifier.text)) { - specifier = resolve( - dirname(sourceFile.fileName), - node.moduleSpecifier.text, - ); - } else { - if (node.moduleSpecifier.text === 'react/jsx-dev-runtime') { - specifier = 'react/cjs/react-jsx-dev-runtime.development'; - } else { - specifier = node.moduleSpecifier.text; - } - } - let importClause: ts.ImportClause | undefined; if ( pushDefault.includes(node.moduleSpecifier.text) && @@ -95,13 +102,20 @@ export class ImportTransformer extends Transformer { importClause = node.importClause; } + logger.debug( + `ImportTransformer Import declaration: ${node.moduleSpecifier.text}`, + ); + return context.factory.updateImportDeclaration( node, node.decorators, node.modifiers, importClause, context.factory.createStringLiteral( - `/Static/import?specifier=${specifier}`, + handleImportSpecifier( + node.moduleSpecifier.text, + dirname(sourceFile.fileName), + ), ), ); } @@ -111,18 +125,6 @@ export class ImportTransformer extends Transformer { node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier) ) { - const relativeTest = /^\.{0,2}[/]/gm; - - let specifier: string; - if (relativeTest.test(node.moduleSpecifier.text)) { - specifier = resolve( - dirname(sourceFile.fileName), - node.moduleSpecifier.text, - ); - } else { - specifier = node.moduleSpecifier.text; - } - /** * Return updated import path to use our Service Worker served path */ @@ -133,7 +135,10 @@ export class ImportTransformer extends Transformer { node.isTypeOnly, node.exportClause, context.factory.createStringLiteral( - `/Static/import?specifier=${specifier}`, + handleImportSpecifier( + node.moduleSpecifier.text, + dirname(sourceFile.fileName), + ), ), ); } diff --git a/src/Modules/WebModule/WebModule.ts b/src/Modules/WebModule/WebModule.ts index 123d530..90399db 100644 --- a/src/Modules/WebModule/WebModule.ts +++ b/src/Modules/WebModule/WebModule.ts @@ -1,36 +1,25 @@ // src/Modules/WebModule/WebModule.ts -import { ObjectType, Field } from 'type-graphql'; - -// const distDir = resolvePath('dist'); -// const mapPath = resolvePath(distDir, 'moduleMap.json'); +import { plainToClass } from 'class-transformer'; +import { IsString, validateOrReject } from 'class-validator'; +import { Field, ObjectType } from 'type-graphql'; +import { Service } from 'typedi'; +@Service() @ObjectType() export class WebModule { + @IsString() @Field() public filePath: string; - @Field() + @Field(() => String) public code: string; - public dependencies = new Set(); + public static async createWebModule( + input: Partial, + ): Promise { + const webModule = plainToClass(WebModule, input); + await validateOrReject(webModule); - public constructor(opts: Partial = {}) { - Object.assign(this, opts); + return webModule; } } - -// let mapData: any; - -// if (process.env.NODE_ENV === 'production') { -// try { -// const { promises: fs } = await import('fs'); - -// const mapFile = await fs.readFile(mapPath); - -// mapData = JSON.parse(mapFile.toString()); -// } catch {} -// } - -// type Specifier = string; - -// export const moduleMap = new Map(mapData); diff --git a/src/Modules/WebModule/WebModuleController.ts b/src/Modules/WebModule/WebModuleController.ts index 87f86bd..d3839b9 100644 --- a/src/Modules/WebModule/WebModuleController.ts +++ b/src/Modules/WebModule/WebModuleController.ts @@ -1,137 +1,109 @@ +/* eslint-disable @typescript-eslint/no-unused-vars-experimental */ // src/Modules/WebModule/WebModuleController.ts - +import { plainToClass } from 'class-transformer'; +import { validateOrReject } from 'class-validator'; +import { Inject, Service } from 'typedi'; +import { fileURLToPath } from 'url'; +import { logger } from '../../Library/Logger'; +import { Queue } from '../Queues/Queue'; +import { QueueController } from '../Queues/QueueController'; +import { RedisController } from '../Redis/RedisController'; +import { RedisType } from '../Redis/RedisTypes'; import { WebModule } from './WebModule'; -import { envMode } from '../../Utils/Environment'; -import { BaseEventEmitter } from '../../Utils/Events'; - -interface WebModuleEventModule { - filePath: string; - - webModule: WebModule; -} - -interface WebModuleEventMap { - newModule: WebModuleEventModule; +import { WebModuleMapJobInput } from './WebModuleMapJobInput'; +import { WebModuleReference } from './WebModuleReference'; - updatedModule: string; -} - -/** - * Controller for managing WebModules - */ -class WebModuleController extends BaseEventEmitter { - public modules = new Map(); - public specifierTest = new Map(); - - public relativePathMatcher = /^\.{0,2}[/]/gm; +type RedisWebModule = WebModule | WebModuleReference; +@Service() +export class WebModuleController { /** - * Matcher to extract submodules under /cjs/MODULE_NAME.${NODE_ENV}.js to fix issues with the CJS to ESM transformer and stuff that has an - * entrypoint with an if block with just require + * Redis Queue for Web Module Map Discovery Tasks */ + private webModuleMapQueue: Queue<'webModuleMapQueue', WebModuleMapJobInput>; + + public constructor( + @Inject(() => QueueController) + private queueController: QueueController, + @Inject(() => RedisController) + private redisController: RedisController, + ) { + this.webModuleMapQueue = queueController.createQueue({ + name: 'webModuleMapQueue', + inputClass: WebModuleMapJobInput, + disableTermination: true, + }); + } - /** - * Maps specifiers to filePaths - */ - public specifierMap = new Map>(); - - private createEventModule(filePath: string): WebModuleEventModule { - // eslint-disable-next-line @typescript-eslint/unbound-method - const getModule = this.getModule; - - class EventModule implements WebModuleEventModule { - public filePath = filePath; - - public get webModule(): WebModule { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return getModule(this.filePath)!; - } + private isJSONString(json: string): false | T { + try { + return JSON.parse(json) as T; + } catch { + return false; } - - return new EventModule(); } - public pushModule(filePath: string, webModule: WebModule): void { - this.emit('newModule', this.createEventModule(filePath)); - - this.createSpecifiers(filePath); + /** + * Spawn Web Module Map Workers to handle the dep map + */ + public async spawnWebModuleWorkers(): Promise { + const workerPathURI = await import.meta.resolve('./WebModuleWorker'); + const workerPath = fileURLToPath(workerPathURI); - this.modules.set(filePath, webModule); - } + /** + * Create the @K-FOSS/TS-ESWorkers. + */ + await this.webModuleMapQueue.createWorkers(workerPath, 1); - public getModule(filePath: string): WebModule | undefined { - return this.modules.get(filePath); + logger.debug( + `TypeScriptController.spawnWebModuleMapWorkers() workerPathURI: ${workerPathURI}`, + ); } - public createSpecifiers(filePath: string): void { - let specifier: string | undefined; - - const cjsMatch = this.testCJS(filePath); - if (cjsMatch) { - specifier = cjsMatch; - } - - const indexMatch = this.testIndex(filePath); - if (indexMatch) { - specifier = indexMatch; - } + public async addWebModuleMapJob( + input: Partial, + ): Promise { + logger.silly('WebModuleController.addWebModuleMapJob()', { + input, + }); - if (!specifier) { - specifier = filePath; - - const moduleSubPath = /\S+\/(node_modules)\/(?(?!\/)\S+)\.((ts|js)x?)$/gm; - const execResult = moduleSubPath.exec(specifier); - - if (execResult?.groups?.module) { - this.setSpecifier(execResult.groups.module, filePath); - } - - this.setSpecifier(specifier.replace(/\.((ts|js)x?)$/gim, ''), filePath); - } + const jobInput = await WebModuleMapJobInput.createWebModuleJobInput(input); - this.setSpecifier(specifier, filePath); + await this.webModuleMapQueue.addTask(jobInput); } - public testCJS(filePath: string): string | undefined { - const cjsMatcher = new RegExp( - `(?(?!/)\\S+)/cjs/(\\k)-?(?\\w+)?.${envMode}.js`, - 'gm', + public async getWebModule(specifier: string): Promise { + const redisEntry = await this.redisController.getValue( + RedisType.MODULE_MAP, + specifier, ); - - const cjsExec = cjsMatcher.exec(filePath); - - if (cjsExec?.groups?.moduleSub) { - return `${cjsExec?.groups?.module}/${cjsExec?.groups?.moduleSub}`; + if (!redisEntry) { + throw new Error('Invalid web module'); } - return cjsExec?.groups?.module; - } + const webModuleInput = this.isJSONString(redisEntry); - public testIndex(filePath: string): string | undefined { - // eslint-disable-next-line prefer-regex-literals - const indexMatcher = new RegExp(`(?(?!/)[\\w,-]+)/index.js`, 'gm'); - const indexExec = indexMatcher.exec(filePath); + if (webModuleInput === false) { + return this.getWebModule(redisEntry); + } else { + const webModule = plainToClass(WebModule, webModuleInput); + await validateOrReject(webModule); - return indexExec?.groups?.module; + return webModule; + } } - public setSpecifier(specifier: string, filePath: string): void { - // const specifierSet = this.specifierMap.get(filePath); - // if (specifierSet) { - // specifierSet.add(specifier); - // } else { - // this.specifierMap.set(filePath, new Set([filePath, specifier])); - // } - - this.specifierTest.set(specifier, filePath); - } + public async setWebModule(input: RedisWebModule): Promise { + if ('webModuleId' in input) { + return this.redisController.setValue(RedisType.MODULE_MAP, { + key: input.specifier, + value: input.webModuleId, + }); + } - public getSpecifiers(filePath: string): string { - const helloWorld = Array.from(this.specifierTest).find( - ([, specifierFilePath]) => specifierFilePath === filePath, - ); - return helloWorld?.[0] || filePath; + return this.redisController.setValue(RedisType.MODULE_MAP, { + key: input.filePath, + value: JSON.stringify(input), + }); } } - -export const webModuleController = new WebModuleController(); diff --git a/src/Modules/WebModule/WebModuleJobInput.ts b/src/Modules/WebModule/WebModuleJobInput.ts new file mode 100644 index 0000000..c8a0271 --- /dev/null +++ b/src/Modules/WebModule/WebModuleJobInput.ts @@ -0,0 +1,25 @@ +// src/Modules/WebModule/WebModuleJobInput.ts +import { plainToClass } from 'class-transformer'; +import { IsString, validateOrReject } from 'class-validator'; + +export class WebModuleJobInput { + @IsString() + public filePath: string; + + @IsString() + public sourceText: string; + + /** + * Create a new Class from params and validate. + * @param inputParams Input Options for Web Module Job + * @returns Promise resolving to validated class + */ + public static async createWebModuleJobInput( + inputParams: Partial, + ): Promise { + const jobInput = plainToClass(WebModuleJobInput, inputParams); + await validateOrReject(jobInput); + + return jobInput; + } +} diff --git a/src/Modules/WebModule/WebModuleMap.ts b/src/Modules/WebModule/WebModuleMap.ts new file mode 100644 index 0000000..d196eb5 --- /dev/null +++ b/src/Modules/WebModule/WebModuleMap.ts @@ -0,0 +1,30 @@ +// src/Modules/WebModule/WebModuleMap.ts +import { plainToClass } from 'class-transformer'; +import { IsString, validateOrReject } from 'class-validator'; +import { ObjectType } from 'type-graphql'; +import {} from 'typedi'; + +@ObjectType() +export class WebModuleMap { + @IsString() + public filePath: string; + + @IsString({ + each: true, + }) + public imports: string[]; + + /** + * Create a Module Map Class object + * @param inputArgs Input paramters for Web Module Map + * @returns Promise resolving to validated Module Map + */ + public static async createModuleMap( + inputArgs: Partial, + ): Promise { + const webModuleMap = plainToClass(WebModuleMap, inputArgs); + await validateOrReject(webModuleMap); + + return webModuleMap; + } +} diff --git a/src/Modules/WebModule/WebModuleMapController.ts b/src/Modules/WebModule/WebModuleMapController.ts new file mode 100644 index 0000000..371f746 --- /dev/null +++ b/src/Modules/WebModule/WebModuleMapController.ts @@ -0,0 +1,57 @@ +// src/Modules/WebModule/WebModuleMapController.ts +import { Inject, Service } from 'typedi'; +import { RedisController } from '../Redis/RedisController'; +import { RedisType } from '../Redis/RedisTypes'; +import { serverOptionsToken, ServerOptions } from '../Server/ServerOptions'; +import { WebModuleMap } from './WebModuleMap'; + +@Service() +export class WebModuleMapController { + public redisController: RedisController; + + public constructor( + @Inject(serverOptionsToken) + public options: ServerOptions, + ) { + this.redisController = new RedisController({ + ...options.redis, + }); + } + + /** + * Set a Module Map Object into Redis + * @param input Web Module Map Input Params + */ + public async setModuleMap(input: WebModuleMap): Promise { + const webModuleMap = await WebModuleMap.createModuleMap(input); + const webModuleMapString = JSON.stringify(webModuleMap); + + return this.redisController.setValue(RedisType.MODULE_MAP, { + key: input.filePath, + value: webModuleMapString, + }); + } + + /** + * Get a Module Map Object from Redis + * @param filePath File Path + */ + public async getModuleMap( + filePath: string, + ): Promise { + const webModuleMapString = await this.redisController.getValue( + RedisType.MODULE_MAP, + filePath, + ); + + if (typeof webModuleMapString === 'string') { + const webModuleMapParams = JSON.parse(webModuleMapString) as WebModuleMap; + + const webModuleMap = await WebModuleMap.createModuleMap( + webModuleMapParams, + ); + + return webModuleMap; + } + } +} diff --git a/src/Modules/WebModule/WebModuleMapJobInput.ts b/src/Modules/WebModule/WebModuleMapJobInput.ts new file mode 100644 index 0000000..e14a991 --- /dev/null +++ b/src/Modules/WebModule/WebModuleMapJobInput.ts @@ -0,0 +1,26 @@ +// src/Modules/WebModules/WebModuleJobInput.ts +import { plainToClass } from 'class-transformer'; +import { IsOptional, IsString, validateOrReject } from 'class-validator'; + +export class WebModuleMapJobInput { + @IsString() + public filePath: string; + + @IsString() + @IsOptional() + public specifier?: string; + + @IsString({ + each: true, + }) + public importedModules?: string[]; + + public static async createWebModuleJobInput( + inputParams: Partial, + ): Promise { + const jobInput = plainToClass(WebModuleMapJobInput, inputParams); + await validateOrReject(jobInput); + + return jobInput; + } +} diff --git a/src/Modules/WebModule/WebModuleReference.ts b/src/Modules/WebModule/WebModuleReference.ts new file mode 100644 index 0000000..8efa269 --- /dev/null +++ b/src/Modules/WebModule/WebModuleReference.ts @@ -0,0 +1,21 @@ +// src/Modules/WebModules/WebModuleReference.ts + +import { plainToClass } from 'class-transformer'; +import { IsString, validateOrReject } from 'class-validator'; + +export class WebModuleReference { + @IsString() + public specifier: string; + + @IsString() + public webModuleId: string; + + public static async createWebModuleReference( + input: Partial, + ): Promise { + const webModuleReference = plainToClass(WebModuleReference, input); + await validateOrReject(webModuleReference); + + return webModuleReference; + } +} diff --git a/src/Modules/WebModule/WebModuleResolver.ts b/src/Modules/WebModule/WebModuleResolver.ts index de3cd55..7e9992a 100644 --- a/src/Modules/WebModule/WebModuleResolver.ts +++ b/src/Modules/WebModule/WebModuleResolver.ts @@ -1,4 +1,4 @@ -// src/Modules/WebModule/WebModuleResolver.ts +// src/Modules/WebModules/WebModuleResolver.ts import { Arg, Field, @@ -8,76 +8,44 @@ import { Resolver, Root, } from 'type-graphql'; -import { logger } from '../../Library/Logger'; +import { Inject, Service } from 'typedi'; +import { RedisController } from '../Redis/RedisController'; +import { RedisType } from '../Redis/RedisTypes'; +import { ServerOptions, serverOptionsToken } from '../Server/ServerOptions'; import { WebModule } from './WebModule'; -import { webModuleController } from './WebModuleController'; +import { WebModuleController } from './WebModuleController'; @InputType() class WebModuleFilter { - @Field({ nullable: true }) + @Field() public specifier: string; - - @Field({ nullable: true }) - public filePath: string; } +@Service() @Resolver(() => WebModule) export class WebModuleResolver { - @Query(() => [WebModule]) - public webModules(): WebModule[] { - return Array.from(webModuleController.modules.values()); - } + @Inject(() => RedisController) + private redisController: RedisController; - @Query(() => WebModule, { nullable: true }) - public webModule( - @Arg('filter') { filePath, specifier }: WebModuleFilter, - ): WebModule | undefined { - if (filePath) { - return webModuleController.getModule(filePath); - } else { - const filePath = webModuleController.specifierTest.get(specifier); + @Inject(() => WebModuleController) + public webModuleController: WebModuleController; - logger.debug( - `WebModuleResolver.webModule() result for ${specifier} is filePath: ${ - filePath?.toString() || '' - }`, - ); + @Inject(serverOptionsToken) + public options: ServerOptions; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return webModuleController.getModule(filePath!); - } - } - - @FieldResolver(() => [WebModule]) - public dependencies(@Root() webModule: WebModule): WebModule[] { - const deps = Array.from(webModule.dependencies); + @Query(() => WebModule) + public async webModule( + @Arg('filter', () => WebModuleFilter) input: WebModuleFilter, + ): Promise { + const webModule = await this.webModuleController.getWebModule( + input.specifier, + ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return deps.flatMap( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (moduleKey) => webModuleController.getModule(moduleKey)!, - )!; + return webModule; } @FieldResolver(() => String) - public specifiers(@Root() webModule: WebModule): string { - return webModuleController.getSpecifiers(webModule.filePath); - } - - @Query(() => Boolean) - public helloTest(): boolean { - logger.debug( - `WebModuleResolver.helloTest() Array.from(webModuleController.modules): ${Array.from( - webModuleController.modules, - )} - Array.from(webModuleController.specifierMap): ${Array.from( - webModuleController.specifierMap, - )} - Array.from(webModuleController.specifierTest): ${Array.from( - webModuleController.specifierTest, - )}`, - ); - - return true; + public async code(@Root() { filePath }: WebModule): Promise { + return this.redisController.getValueOrThrow(RedisType.MODULE, filePath); } } diff --git a/src/Modules/WebModule/WebModuleRoute.ts b/src/Modules/WebModule/WebModuleRoute.ts deleted file mode 100644 index b6adf21..0000000 --- a/src/Modules/WebModule/WebModuleRoute.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ -// src/Server/Modules/WebModule/ModuleRoute.ts -import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; -import { Service } from 'typedi'; -import { Route } from '../../Library/Fastify'; -import { webModuleController } from './WebModuleController'; - -@Service() -export default class WebModuleRoute implements Route { - public options: Route['options'] = { - method: 'GET', - url: '/Static/*', - }; - - // eslint-disable-next-line @typescript-eslint/require-await - public async handler( - this: FastifyInstance, - request: FastifyRequest<{ Params: { '*': string } }>, - reply: FastifyReply, - ): Promise { - const filePath = request.params['*']; - if (!filePath) { - const err = (new Error() as unknown) as { - statusCode: number; - message: string; - }; - err.statusCode = 400; - err.message = 'Invalid file path'; - throw err; - } - - const moduleFilePath = filePath.startsWith('/') ? filePath : `/${filePath}`; - - const webModule = webModuleController.getModule(moduleFilePath); - - if (!webModule) { - const err = (new Error() as unknown) as { - statusCode: number; - message: string; - }; - err.statusCode = 404; - err.message = 'Invalid file'; - throw err; - } - - reply.type('text/javascript'); - reply.header('Service-Worker-Allowed', '/'); - - return webModule.code; - } -} diff --git a/src/Modules/WebModule/WebModuleWorker.ts b/src/Modules/WebModule/WebModuleWorker.ts new file mode 100644 index 0000000..6de5457 --- /dev/null +++ b/src/Modules/WebModule/WebModuleWorker.ts @@ -0,0 +1,119 @@ +// src/Modules/WebModule/WebModuleWorker.ts +import { Queue, Worker } from 'bullmq'; +import { Container } from 'typedi'; +import { threadId } from 'worker_threads'; +import { logger } from '../../Library/Logger'; +import '../../Utils/Setup'; +import { timeout } from '../../Utils/timeout'; +import { queueToken } from '../Queues/QueueToken'; +import { workerControllerToken } from '../Queues/WorkerController'; +import { workerInputToken } from '../Queues/WorkerInput'; +import { RedisController } from '../Redis/RedisController'; +import { WebModuleController } from './WebModuleController'; +import { WebModuleJobInput } from './WebModuleJobInput'; +import { WebModuleMapJobInput } from './WebModuleMapJobInput'; +import { WebModuleReference } from './WebModuleReference'; + +const workerController = Container.get(workerControllerToken); +const queueName = Container.get(queueToken); +const workerInput = Container.get(workerInputToken); + +const webModuleController = Container.get(WebModuleController); + +workerController.logger.info(`Worker starting`, { + worker: 'TypeScriptTranspilerWorker.ts', + workeId: threadId, +}); + +workerController.logger.info(`Worker starting`); + +const redisController = Container.get(RedisController); + +workerController.logger.debug(`Retrieved workerData:`, { + objectName: 'workerInput', + workerInput, +}); + +const webModuleMapQue = new Queue(queueName, { + ...workerInput.queueOptions, +}); + +workerController.logger.silly(`webModuleQue has been created`, { + webModuleMapQue, +}); + +function getModuleAlias(filePath: string): string[] { + const strippedFilePath = filePath.replace(/\.(js|ts)x?/, ''); + + const aliases: string[] = [strippedFilePath]; + + if (strippedFilePath.endsWith('/index')) { + aliases.push(strippedFilePath.replace('/index', '')); + } + + return aliases; +} + +const moduleMapWorker = new Worker( + workerInput.queName, + async (job) => { + workerController.logger.silly(`New Web Module Map Job:`, { + data: job.data, + }); + + const jobData = await WebModuleMapJobInput.createWebModuleJobInput( + job.data, + ); + + const aliases = [...getModuleAlias(jobData.filePath), jobData.specifier]; + + workerController.logger.silly(`webModule Aliases`, { + aliases, + }); + + await Promise.all( + aliases.filter(Boolean).map(async (alias) => { + const webModuleAlias = await WebModuleReference.createWebModuleReference( + { + specifier: alias, + webModuleId: jobData.filePath, + }, + ); + return webModuleController.setWebModule(webModuleAlias); + }), + ); + + logger.silly('Web Module Map jobData', { + jobData, + }); + + await timeout(60); + }, + { + connection: workerInput.queueOptions.connection, + }, +); + +workerController.logger.silly(`moduleMapWorker`, { + moduleMapWorker, +}); + +const moduleWorker = new Worker( + 'webModuleQueue', + async (job) => { + workerController.logger.silly(`New Web Module Job:`, { + data: job.data.filePath, + }); + + const input = await WebModuleJobInput.createWebModuleJobInput(job.data); + + await redisController.IORedis.set(input.filePath, input.sourceText); + }, + { + connection: workerInput.queueOptions.connection, + }, +); + +workerController.logger.silly(`moduleWorker`, { + moduleWorker, +}); diff --git a/src/Utils/Types.ts b/src/Utils/Types.ts new file mode 100644 index 0000000..71a58c0 --- /dev/null +++ b/src/Utils/Types.ts @@ -0,0 +1 @@ +// src/Utils/Types.ts diff --git a/src/Web_Test/App.tsx b/src/Web_Test/App.tsx index d5a023e..7abe1e7 100644 --- a/src/Web_Test/App.tsx +++ b/src/Web_Test/App.tsx @@ -1,14 +1,12 @@ // src/App.tsx -import React from 'react'; -import { Counter } from './Components/Counter/index'; +import { Nav } from './Components/Nav'; +import { Router } from './Components/Routers'; export function App(): React.ReactElement { - const sayHello = React.useCallback(() => console.log('HelloWorld'), []); - return ( <> -

HelloWorld

- +