diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index f26ecaa..bf02010 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -16,9 +16,6 @@ It is suggested to install this globally[@xarc/run-cli](https://www.npmjs.com/pa This will allow you to run varios tasks ![xrun auto complete](./screenshots/xrun-autocomplete.png) -In devmode you can open [local remote devtools](http://localhost:8000) -![remote redux devtools](./screenshots/remote-redux-devtools.png) - ## Development instructions ``` diff --git a/package-lock.json b/package-lock.json index 3515542..fba2e86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,6 @@ "@types/react-dom": "^18.0.10", "@types/react-redux": "^7.1.25", "@types/redux-logger": "^3.0.9", - "@types/remote-redux-devtools": "^0.5.4", "@types/shelljs": "^0.8.8", "@types/sinon-chrome": "^2.2.11", "@types/wait-on": "^5.2.0", @@ -85,7 +84,6 @@ "prettier": "^2.8.2", "prettier-plugin-packagejson": "^2.3.0", "prettier-plugin-sort-json": "1.0.0", - "remote-redux-devtools": "^0.5.16", "rimraf": "^3.0.2", "shelljs": "^0.8.5", "sinon": "^15.0.1", @@ -3928,15 +3926,6 @@ "redux": "^4.0.0" } }, - "node_modules/@types/remote-redux-devtools": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@types/remote-redux-devtools/-/remote-redux-devtools-0.5.5.tgz", - "integrity": "sha512-Xuya1TegRPAe92+nnEeYpfufE/mtfN99+GH272edaoWohbMA+yP6r+wYqK4sq/fvmoUPtPHtwZR2Mkk+6uHeBQ==", - "dev": true, - "dependencies": { - "redux": "^4.0.0" - } - }, "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -7598,15 +7587,6 @@ "node": ">=8" } }, - "node_modules/clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha512-h5FLmEMFHeuzqmpVRcDayNlVZ+k4uK1niyKQN6oUMe7ieJihv44Vc3dY/kDnnWX4PDQSwes48s965PG/D4GntQ==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -7776,12 +7756,6 @@ "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", "dev": true }, - "node_modules/component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -11054,12 +11028,6 @@ "node": ">=8.0.0" } }, - "node_modules/get-params": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/get-params/-/get-params-0.1.2.tgz", - "integrity": "sha512-41eOxtlGgHQRbFyA8KTH+w+32Em3cRdfBud7j67ulzmIfmaHX9doq47s0fa4P5o9H64BZX9nrYI6sJvk46Op+Q==", - "dev": true - }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -14927,12 +14895,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsan": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/jsan/-/jsan-3.1.14.tgz", - "integrity": "sha512-wStfgOJqMv4QKktuH273f5fyi3D3vy2pHOiSDGPvpcS/q+wb/M7AK3vkCcaHbkZxDOlDU/lDJgccygKSG2OhtA==", - "dev": true - }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -15553,12 +15515,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "node_modules/linked-list": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/linked-list/-/linked-list-0.1.0.tgz", - "integrity": "sha512-Zr4ovrd0ODzF3ut2TWZMdHIxb8iFdJc/P3QM4iCJdlxxGHXo69c9hGIHzLo8/FtuR9E6WUZc5irKhtPUgOKMAg==", - "dev": true - }, "node_modules/lint-staged": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", @@ -17104,12 +17060,6 @@ "dev": true, "optional": true }, - "node_modules/nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", - "dev": true - }, "node_modules/natives": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", @@ -18770,16 +18720,6 @@ "teleport": ">=0.2.0" } }, - "node_modules/querystring": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", - "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/querystring-es3": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", @@ -19163,34 +19103,6 @@ "@babel/runtime": "^7.9.2" } }, - "node_modules/redux-devtools-core": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/redux-devtools-core/-/redux-devtools-core-0.2.1.tgz", - "integrity": "sha512-RAGOxtUFdr/1USAvxrWd+Gq/Euzgw7quCZlO5TgFpDfG7rB5tMhZUrNyBjpzgzL2yMk0eHnPYIGm7NkIfRzHxQ==", - "deprecated": "Package moved to @redux-devtools/app.", - "dev": true, - "dependencies": { - "get-params": "^0.1.2", - "jsan": "^3.1.13", - "lodash": "^4.17.11", - "nanoid": "^2.0.0", - "remotedev-serialize": "^0.1.8" - } - }, - "node_modules/redux-devtools-instrument": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/redux-devtools-instrument/-/redux-devtools-instrument-1.10.0.tgz", - "integrity": "sha512-X8JRBCzX2ADSMp+iiV7YQ8uoTNyEm0VPFPd4T854coz6lvRiBrFSqAr9YAS2n8Kzxx8CJQotR0QF9wsMM+3DvA==", - "deprecated": "Package moved to @redux-devtools/instrument.", - "dev": true, - "dependencies": { - "lodash": "^4.17.19", - "symbol-observable": "^1.2.0" - }, - "peerDependencies": { - "redux": "^3.4.0 || ^4.0.0" - } - }, "node_modules/redux-logger": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-4.0.0.tgz", @@ -19355,30 +19267,6 @@ "node": ">= 0.10.0" } }, - "node_modules/remote-redux-devtools": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/remote-redux-devtools/-/remote-redux-devtools-0.5.16.tgz", - "integrity": "sha512-xZ2D1VRIWzat5nsvcraT6fKEX9Cfi+HbQBCwzNnUAM8Uicm/anOc60XGalcaDPrVmLug7nhDl2nimEa3bL3K9w==", - "dev": true, - "dependencies": { - "jsan": "^3.1.13", - "querystring": "^0.2.0", - "redux-devtools-core": "^0.2.1", - "redux-devtools-instrument": "^1.9.4", - "rn-host-detect": "^1.1.5", - "socketcluster-client": "^14.2.1" - } - }, - "node_modules/remotedev-serialize": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/remotedev-serialize/-/remotedev-serialize-0.1.9.tgz", - "integrity": "sha512-5tFdZg9mSaAWTv6xmQ7HtHjKMLSFQFExEZOtJe10PLsv1wb7cy7kYHtBvTYRro27/3fRGEcQBRNKSaixOpb69w==", - "deprecated": "Package moved to @redux-devtools/serialize.", - "dev": true, - "dependencies": { - "jsan": "^3.1.13" - } - }, "node_modules/require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -19563,12 +19451,6 @@ "inherits": "^2.0.1" } }, - "node_modules/rn-host-detect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz", - "integrity": "sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==", - "dev": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -19675,27 +19557,6 @@ "node": ">=v12.22.7" } }, - "node_modules/sc-channel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/sc-channel/-/sc-channel-1.2.0.tgz", - "integrity": "sha512-M3gdq8PlKg0zWJSisWqAsMmTVxYRTpVRqw4CWAdKBgAfVKumFcTjoCV0hYu7lgUXccCtCD8Wk9VkkE+IXCxmZA==", - "dev": true, - "dependencies": { - "component-emitter": "1.2.1" - } - }, - "node_modules/sc-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-2.0.1.tgz", - "integrity": "sha512-JoVhq3Ud+3Ujv2SIG7W0XtjRHsrNgl6iXuHHsh0s+Kdt5NwI6N2EGAZD4iteitdDv68ENBkpjtSvN597/wxPSQ==", - "dev": true - }, - "node_modules/sc-formatter": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sc-formatter/-/sc-formatter-3.0.3.tgz", - "integrity": "sha512-lYI/lTs1u1c0geKElcj+bmEUfcP/HuKg2iDeTijPSjiTNFzN3Cf8Qh6tVd65oi7Qn+2/oD7LP4s6GC13v/9NiQ==", - "dev": true - }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -20378,55 +20239,6 @@ "npm": ">= 3.0.0" } }, - "node_modules/socketcluster-client": { - "version": "14.3.2", - "resolved": "https://registry.npmjs.org/socketcluster-client/-/socketcluster-client-14.3.2.tgz", - "integrity": "sha512-xDtgW7Ss0ARlfhx53bJ5GY5THDdEOeJnT+/C9Rmrj/vnZr54xeiQfrCZJbcglwe732nK3V+uZq87IvrRl7Hn4g==", - "dev": true, - "dependencies": { - "buffer": "^5.2.1", - "clone": "2.1.1", - "component-emitter": "1.2.1", - "linked-list": "0.1.0", - "querystring": "0.2.0", - "sc-channel": "^1.2.0", - "sc-errors": "^2.0.1", - "sc-formatter": "^3.0.1", - "uuid": "3.2.1", - "ws": "^7.5.0" - } - }, - "node_modules/socketcluster-client/node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/socketcluster-client/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -21023,15 +20835,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -22293,16 +22096,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -26299,15 +26092,6 @@ "redux": "^4.0.0" } }, - "@types/remote-redux-devtools": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@types/remote-redux-devtools/-/remote-redux-devtools-0.5.5.tgz", - "integrity": "sha512-Xuya1TegRPAe92+nnEeYpfufE/mtfN99+GH272edaoWohbMA+yP6r+wYqK4sq/fvmoUPtPHtwZR2Mkk+6uHeBQ==", - "dev": true, - "requires": { - "redux": "^4.0.0" - } - }, "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", @@ -29090,12 +28874,6 @@ } } }, - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha512-h5FLmEMFHeuzqmpVRcDayNlVZ+k4uK1niyKQN6oUMe7ieJihv44Vc3dY/kDnnWX4PDQSwes48s965PG/D4GntQ==", - "dev": true - }, "clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -29241,12 +29019,6 @@ "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", "dev": true }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==", - "dev": true - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -31763,12 +31535,6 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, - "get-params": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/get-params/-/get-params-0.1.2.tgz", - "integrity": "sha512-41eOxtlGgHQRbFyA8KTH+w+32Em3cRdfBud7j67ulzmIfmaHX9doq47s0fa4P5o9H64BZX9nrYI6sJvk46Op+Q==", - "dev": true - }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -34619,12 +34385,6 @@ "esprima": "^4.0.0" } }, - "jsan": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/jsan/-/jsan-3.1.14.tgz", - "integrity": "sha512-wStfgOJqMv4QKktuH273f5fyi3D3vy2pHOiSDGPvpcS/q+wb/M7AK3vkCcaHbkZxDOlDU/lDJgccygKSG2OhtA==", - "dev": true - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -35133,12 +34893,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "linked-list": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/linked-list/-/linked-list-0.1.0.tgz", - "integrity": "sha512-Zr4ovrd0ODzF3ut2TWZMdHIxb8iFdJc/P3QM4iCJdlxxGHXo69c9hGIHzLo8/FtuR9E6WUZc5irKhtPUgOKMAg==", - "dev": true - }, "lint-staged": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.1.0.tgz", @@ -36316,12 +36070,6 @@ "dev": true, "optional": true }, - "nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", - "dev": true - }, "natives": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", @@ -37576,12 +37324,6 @@ "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", "dev": true }, - "querystring": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", - "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", - "dev": true - }, "querystring-es3": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", @@ -37875,29 +37617,6 @@ "@babel/runtime": "^7.9.2" } }, - "redux-devtools-core": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/redux-devtools-core/-/redux-devtools-core-0.2.1.tgz", - "integrity": "sha512-RAGOxtUFdr/1USAvxrWd+Gq/Euzgw7quCZlO5TgFpDfG7rB5tMhZUrNyBjpzgzL2yMk0eHnPYIGm7NkIfRzHxQ==", - "dev": true, - "requires": { - "get-params": "^0.1.2", - "jsan": "^3.1.13", - "lodash": "^4.17.11", - "nanoid": "^2.0.0", - "remotedev-serialize": "^0.1.8" - } - }, - "redux-devtools-instrument": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/redux-devtools-instrument/-/redux-devtools-instrument-1.10.0.tgz", - "integrity": "sha512-X8JRBCzX2ADSMp+iiV7YQ8uoTNyEm0VPFPd4T854coz6lvRiBrFSqAr9YAS2n8Kzxx8CJQotR0QF9wsMM+3DvA==", - "dev": true, - "requires": { - "lodash": "^4.17.19", - "symbol-observable": "^1.2.0" - } - }, "redux-logger": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-4.0.0.tgz", @@ -38023,29 +37742,6 @@ "commander": "^2.6.0" } }, - "remote-redux-devtools": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/remote-redux-devtools/-/remote-redux-devtools-0.5.16.tgz", - "integrity": "sha512-xZ2D1VRIWzat5nsvcraT6fKEX9Cfi+HbQBCwzNnUAM8Uicm/anOc60XGalcaDPrVmLug7nhDl2nimEa3bL3K9w==", - "dev": true, - "requires": { - "jsan": "^3.1.13", - "querystring": "^0.2.0", - "redux-devtools-core": "^0.2.1", - "redux-devtools-instrument": "^1.9.4", - "rn-host-detect": "^1.1.5", - "socketcluster-client": "^14.2.1" - } - }, - "remotedev-serialize": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/remotedev-serialize/-/remotedev-serialize-0.1.9.tgz", - "integrity": "sha512-5tFdZg9mSaAWTv6xmQ7HtHjKMLSFQFExEZOtJe10PLsv1wb7cy7kYHtBvTYRro27/3fRGEcQBRNKSaixOpb69w==", - "dev": true, - "requires": { - "jsan": "^3.1.13" - } - }, "require-at": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", @@ -38181,12 +37877,6 @@ "inherits": "^2.0.1" } }, - "rn-host-detect": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz", - "integrity": "sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==", - "dev": true - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -38256,27 +37946,6 @@ "xmlchars": "^2.2.0" } }, - "sc-channel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/sc-channel/-/sc-channel-1.2.0.tgz", - "integrity": "sha512-M3gdq8PlKg0zWJSisWqAsMmTVxYRTpVRqw4CWAdKBgAfVKumFcTjoCV0hYu7lgUXccCtCD8Wk9VkkE+IXCxmZA==", - "dev": true, - "requires": { - "component-emitter": "1.2.1" - } - }, - "sc-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-2.0.1.tgz", - "integrity": "sha512-JoVhq3Ud+3Ujv2SIG7W0XtjRHsrNgl6iXuHHsh0s+Kdt5NwI6N2EGAZD4iteitdDv68ENBkpjtSvN597/wxPSQ==", - "dev": true - }, - "sc-formatter": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sc-formatter/-/sc-formatter-3.0.3.tgz", - "integrity": "sha512-lYI/lTs1u1c0geKElcj+bmEUfcP/HuKg2iDeTijPSjiTNFzN3Cf8Qh6tVd65oi7Qn+2/oD7LP4s6GC13v/9NiQ==", - "dev": true - }, "scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -38816,39 +38485,6 @@ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, - "socketcluster-client": { - "version": "14.3.2", - "resolved": "https://registry.npmjs.org/socketcluster-client/-/socketcluster-client-14.3.2.tgz", - "integrity": "sha512-xDtgW7Ss0ARlfhx53bJ5GY5THDdEOeJnT+/C9Rmrj/vnZr54xeiQfrCZJbcglwe732nK3V+uZq87IvrRl7Hn4g==", - "dev": true, - "requires": { - "buffer": "^5.2.1", - "clone": "2.1.1", - "component-emitter": "1.2.1", - "linked-list": "0.1.0", - "querystring": "0.2.0", - "sc-channel": "^1.2.0", - "sc-errors": "^2.0.1", - "sc-formatter": "^3.0.1", - "uuid": "3.2.1", - "ws": "^7.5.0" - }, - "dependencies": { - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "dev": true - }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "requires": {} - } - } - }, "socks": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", @@ -39335,12 +38971,6 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, - "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 - }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -40277,12 +39907,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/package.json b/package.json index 2a3bdf3..ca56ee3 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,6 @@ "@types/react-dom": "^18.0.10", "@types/react-redux": "^7.1.25", "@types/redux-logger": "^3.0.9", - "@types/remote-redux-devtools": "^0.5.4", "@types/shelljs": "^0.8.8", "@types/sinon-chrome": "^2.2.11", "@types/wait-on": "^5.2.0", @@ -106,7 +105,6 @@ "prettier": "^2.8.2", "prettier-plugin-packagejson": "^2.3.0", "prettier-plugin-sort-json": "1.0.0", - "remote-redux-devtools": "^0.5.16", "rimraf": "^3.0.2", "shelljs": "^0.8.5", "sinon": "^15.0.1", diff --git a/src/core/WakaTimeCore.ts b/src/core/WakaTimeCore.ts index 7f6fa99..7284271 100644 --- a/src/core/WakaTimeCore.ts +++ b/src/core/WakaTimeCore.ts @@ -8,6 +8,8 @@ import config from '../config/config'; import { SendHeartbeat } from '../types/heartbeats'; import { GrandTotal, SummariesPayload } from '../types/summaries'; import { ApiKeyPayload, AxiosUserResponse, User } from '../types/user'; +import { generateProjectFromDevSites, IS_FIREFOX } from '../utils'; +import { getApiKey } from '../utils/apiKey'; import changeExtensionState from '../utils/changeExtensionState'; import contains from '../utils/contains'; import getDomainFromUrl from '../utils/getDomainFromUrl'; @@ -106,20 +108,12 @@ class WakaTimeCore { return userPayload.data.data; } - async getApiKey(): Promise { - const storage = await browser.storage.sync.get({ - apiKey: config.apiKey, - }); - const apiKey = storage.apiKey as string; - return apiKey; - } - /** * Depending on various factors detects the current active tab URL or domain, * and sends it to WakaTime for logging. */ async recordHeartbeat(): Promise { - const apiKey = await this.getApiKey(); + const apiKey = await getApiKey(); if (!apiKey) { return changeExtensionState('notLogging'); } @@ -151,7 +145,7 @@ class WakaTimeCore { } // Checks dev websites - const project = this.generateProjectFromDevSites(currentActiveTab.url as string); + const project = generateProjectFromDevSites(currentActiveTab.url as string); if (items.loggingStyle == 'blacklist') { if (!contains(currentActiveTab.url as string, items.blacklist as string)) { @@ -303,17 +297,6 @@ class WakaTimeCore { return items.loggingType; } - generateProjectFromDevSites(url: string): string | null { - const githubUrls = ['https://github.com/', 'https://github.dev/']; - for (const githubUrl of githubUrls) { - if (url.startsWith(githubUrl)) { - const newUrl = url.replace(githubUrl, ''); - return newUrl.split('/')[1] || null; - } - } - return null; - } - /** * Creates payload for the heartbeat and returns it as JSON. * @@ -326,7 +309,7 @@ class WakaTimeCore { preparePayload(heartbeat: SendHeartbeat, type: string): Record { let browserName = 'chrome'; let userAgent; - if (navigator.userAgent.includes('Firefox')) { + if (IS_FIREFOX) { browserName = 'firefox'; userAgent = navigator.userAgent.match(/Firefox\/\S+/g)![0]; } else { @@ -393,7 +376,7 @@ class WakaTimeCore { * @param requests */ async sendCachedHeartbeatsRequest(): Promise { - const apiKey = await this.getApiKey(); + const apiKey = await getApiKey(); if (!apiKey) { return changeExtensionState('notLogging'); } diff --git a/src/manifests/chrome.json b/src/manifests/chrome.json index e6b60c8..019accc 100644 --- a/src/manifests/chrome.json +++ b/src/manifests/chrome.json @@ -11,6 +11,13 @@ "service_worker": "background.js", "type": "module" }, + "content_scripts": [ + { + "matches": [""], + "js": ["wakatimeScript.js"], + "run_at": "document_end" + } + ], "description": "Automatic time tracking for Chrome.", "devtools_page": "devtools.html", "homepage_url": "https://wakatime.com", @@ -26,5 +33,5 @@ "page": "options.html" }, "permissions": ["alarms", "tabs", "storage", "idle"], - "version": "3.0.8" + "version": "3.0.9" } diff --git a/src/manifests/firefox.json b/src/manifests/firefox.json index 46121eb..4cddeb3 100644 --- a/src/manifests/firefox.json +++ b/src/manifests/firefox.json @@ -17,6 +17,13 @@ "strict_min_version": "48.0" } }, + "content_scripts": [ + { + "matches": [""], + "js": ["wakatimeScript.js"], + "run_at": "document_end" + } + ], "description": "Automatic time tracking for Chrome.", "devtools_page": "devtools.html", "homepage_url": "https://wakatime.com", @@ -39,5 +46,5 @@ "storage", "idle" ], - "version": "3.0.8" + "version": "3.0.9" } diff --git a/src/stores/createStore.ts b/src/stores/createStore.ts index 770da04..acac09e 100644 --- a/src/stores/createStore.ts +++ b/src/stores/createStore.ts @@ -1,10 +1,8 @@ -import { configureStore, Store, combineReducers } from '@reduxjs/toolkit'; -import { logger } from 'redux-logger'; import { reduxBatch } from '@manaflair/redux-batch'; -import devToolsEnhancer from 'remote-redux-devtools'; -import currentUserReducer, { initialState as InitalCurrentUser } from '../reducers/currentUser'; +import { combineReducers, configureStore, Store } from '@reduxjs/toolkit'; +import { logger } from 'redux-logger'; import configReducer, { initialConfigState } from '../reducers/configReducer'; -import isProd from '../utils/isProd'; +import currentUserReducer, { initialState as InitalCurrentUser } from '../reducers/currentUser'; // Create the root reducer separately so we can extract the RootState type const rootReducer = combineReducers({ @@ -19,14 +17,9 @@ const preloadedState: RootState = { currentUser: InitalCurrentUser, }; -export default (appName: string): Store => { +export default (): Store => { const enhancers = []; enhancers.push(reduxBatch); - if (!isProd()) { - enhancers.push( - devToolsEnhancer({ hostname: 'localhost', name: appName, port: 8000, realtime: true }), - ); - } const store = configureStore({ devTools: true, enhancers, diff --git a/src/utils/apiKey.ts b/src/utils/apiKey.ts index 56c86e4..8414635 100644 --- a/src/utils/apiKey.ts +++ b/src/utils/apiKey.ts @@ -1,3 +1,6 @@ +import browser from 'webextension-polyfill'; +import config from '../config/config'; + export default function apiKeyInvalid(key?: string): string { const err = 'Invalid api key... check https://wakatime.com/settings for your key'; if (!key) return err; @@ -8,3 +11,11 @@ export default function apiKeyInvalid(key?: string): string { if (!re.test(key)) return err; return ''; } + +export async function getApiKey(): Promise { + const storage = await browser.storage.sync.get({ + apiKey: config.apiKey, + }); + const apiKey = storage.apiKey as string; + return apiKey; +} diff --git a/src/utils/changeExtensionIcon.ts b/src/utils/changeExtensionIcon.ts index 650634b..40c6199 100644 --- a/src/utils/changeExtensionIcon.ts +++ b/src/utils/changeExtensionIcon.ts @@ -1,5 +1,6 @@ import browser from 'webextension-polyfill'; import config from '../config/config'; +import { IS_FIREFOX } from '.'; type ColorIconTypes = 'gray' | 'red' | 'white' | ''; @@ -21,7 +22,7 @@ export default async function changeExtensionIcon(color?: ColorIconTypes): Promi } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (browser.browserAction) { + if (IS_FIREFOX) { await browser.browserAction.setIcon({ path: path }); // Support for FF with manifest V2 } else { await browser.action.setIcon({ path: path }); // Support for Chrome with manifest V3 diff --git a/src/utils/changeExtensionTooltip.ts b/src/utils/changeExtensionTooltip.ts index d6cc4ae..432fef8 100644 --- a/src/utils/changeExtensionTooltip.ts +++ b/src/utils/changeExtensionTooltip.ts @@ -1,5 +1,6 @@ import browser from 'webextension-polyfill'; import config from '../config/config'; +import { IS_FIREFOX } from '.'; /** * It changes the extension title @@ -13,7 +14,7 @@ export default async function changeExtensionTooltip(text: string): Promise { + const githubUrls = ['https://github.com/', 'https://github.dev/']; + for (const githubUrl of githubUrls) { + if (url.startsWith(githubUrl)) { + const newUrl = url.replace(githubUrl, ''); + return newUrl.split('/')[1] || null; + } + } + return null; +}; diff --git a/src/wakatimeScript.ts b/src/wakatimeScript.ts new file mode 100644 index 0000000..0f1a0f3 --- /dev/null +++ b/src/wakatimeScript.ts @@ -0,0 +1,289 @@ +import moment from 'moment'; +import browser from 'webextension-polyfill'; +import config from './config/config'; +import { SendHeartbeat } from './types/heartbeats'; +import { generateProjectFromDevSites, IS_FIREFOX } from './utils'; +import { getApiKey } from './utils/apiKey'; +import contains from './utils/contains'; +import getDomainFromUrl from './utils/getDomainFromUrl'; + +const twoMinutes = 120000; + +/** + * Creates an array from list using \n as delimiter + * and checks if any element in list is contained in the url. + * Also checks if element is assigned to a project using @@ as delimiter + * + * @param url + * @param list + * @returns {object} + */ +const getHeartbeat = (url: string, list: string) => { + const projectIndicatorCharacters = '@@'; + + const lines = list.split('\n'); + for (let i = 0; i < lines.length; i++) { + // strip (http:// or https://) and trailing (`/` or `@@`) + const cleanLine = lines[i] + .trim() + .replace(/(\/|@@)$/, '') + .replace(/^(?:https?:\/\/)?/i, ''); + if (cleanLine === '') continue; + + const projectIndicatorIndex = cleanLine.lastIndexOf(projectIndicatorCharacters); + const projectIndicatorExists = projectIndicatorIndex > -1; + let projectName = null; + let urlFromLine = cleanLine; + if (projectIndicatorExists) { + const start = projectIndicatorIndex + projectIndicatorCharacters.length; + projectName = cleanLine.substring(start); + urlFromLine = cleanLine + .replace(cleanLine.substring(projectIndicatorIndex), '') + .replace(/\/$/, ''); + } + const schemaHttpExists = url.match(/^http:\/\//i); + const schemaHttpsExists = url.match(/^https:\/\//i); + let schema = ''; + if (schemaHttpExists) { + schema = 'http://'; + } + if (schemaHttpsExists) { + schema = 'https://'; + } + const cleanUrl = url + .trim() + .replace(/(\/|@@)$/, '') + .replace(/^(?:https?:\/\/)?/i, ''); + const startsWithUrl = cleanUrl.toLowerCase().includes(urlFromLine.toLowerCase()); + if (startsWithUrl) { + return { + project: projectName, + url: schema + urlFromLine, + }; + } + + const lineRe = new RegExp(cleanLine.replace('.', '.').replace('*', '.*')); + + // If url matches the current line return true + if (lineRe.test(url)) { + return { + project: null, + url: schema + urlFromLine, + }; + } + } + + return { + project: null, + url: null, + }; +}; + +/** + * Sends AJAX request with payload to the heartbeat API as JSON. + * + * @param payload + * @param method + * @returns {*} + */ +const sendPostRequestToApi = async ( + payload: Record, + apiKey = '', + hostname = '', +): Promise => { + try { + const request: RequestInit = { + body: JSON.stringify(payload), + credentials: 'omit', + method: 'POST', + }; + if (hostname) { + request.headers = { + 'X-Machine-Name': hostname, + }; + } + const response = await fetch(`${config.heartbeatApiUrl}?api_key=${apiKey}`, request); + await response.json(); + } catch (err: unknown) { + console.log('Error', err); + } +}; + +/** + * Creates payload for the heartbeat and returns it as JSON. + * + * @param heartbeat + * @param type + * @param debug + * @returns {*} + * @private + */ +const preparePayload = (heartbeat: SendHeartbeat, type: string): Record => { + let browserName = 'chrome'; + let userAgent; + if (IS_FIREFOX) { + browserName = 'firefox'; + userAgent = navigator.userAgent.match(/Firefox\/\S+/g)![0]; + } else { + userAgent = navigator.userAgent.match(/Chrome\/\S+/g)![0]; + } + const payload: Record = { + entity: heartbeat.url, + time: moment().format('X'), + type: type, + user_agent: `${userAgent} ${browserName}-wakatime/${config.version}`, + }; + + if (heartbeat.project) { + payload.project = heartbeat.project; + } + + return payload; +}; + +/** + * Returns a promise with logging type variable. + * + * @returns {*} + * @private + */ +const getLoggingType = async (): Promise => { + const items = await browser.storage.sync.get({ + loggingType: config.loggingType, + }); + + return items.loggingType; +}; + +/** + * Given the heartbeat and logging type it creates a payload and + * sends an ajax post request to the API. + * + * @param heartbeat + * @param debug + */ +const sendHeartbeat = async ( + heartbeat: SendHeartbeat, + apiKey: string, + navigationPayload: Record, +): Promise => { + let payload; + + const loggingType = await getLoggingType(); + // Get only the domain from the entity. + // And send that in heartbeat + if (loggingType == 'domain') { + heartbeat.url = getDomainFromUrl(heartbeat.url); + payload = preparePayload(heartbeat, 'domain'); + await sendPostRequestToApi({ ...payload, ...navigationPayload }, apiKey, heartbeat.hostname); + } + // Send entity in heartbeat + else if (loggingType == 'url') { + payload = preparePayload(heartbeat, 'url'); + await sendPostRequestToApi({ ...payload, ...navigationPayload }, apiKey, heartbeat.hostname); + } +}; + +/** + * Depending on various factors detects the current active tab URL or domain, + * and sends it to WakaTime for logging. + */ +const recordHeartbeat = async (apiKey: string, payload: Record): Promise => { + const items = await browser.storage.sync.get({ + blacklist: '', + hostname: config.hostname, + loggingEnabled: config.loggingEnabled, + loggingStyle: config.loggingStyle, + socialMediaSites: config.socialMediaSites, + trackSocialMedia: config.trackSocialMedia, + whitelist: '', + }); + if (items.loggingEnabled === true) { + if (!items.trackSocialMedia) { + if (contains(document.URL, items.socialMediaSites as string)) { + return; + } + } + + // Checks dev websites + const project = generateProjectFromDevSites(document.URL); + + if (items.loggingStyle == 'blacklist') { + if (!contains(document.URL, items.blacklist as string)) { + await sendHeartbeat( + { + hostname: items.hostname as string, + project, + url: document.URL, + }, + apiKey, + payload, + ); + } else { + console.log(`${document.URL} is on a blacklist.`); + } + } + + if (items.loggingStyle == 'whitelist') { + const heartbeat = getHeartbeat(document.URL, items.whitelist as string); + if (heartbeat.url) { + await sendHeartbeat( + { + ...heartbeat, + hostname: items.hostname as string, + project: heartbeat.project ?? project, + }, + apiKey, + payload, + ); + } else { + console.log(`${document.URL} is not on a whitelist.`); + } + } + } +}; + +const init = async () => { + const apiKey = await getApiKey(); + if (!apiKey) return; + + const { hostname } = document.location; + const canvaProject = document.getElementsByClassName('rF765A'); + + if (hostname === 'www.canva.com' && canvaProject.length > 0) { + const ogTitle = (document.head.querySelector('meta[property="og:title"]') as HTMLMetaElement) + .content; + await recordHeartbeat(apiKey, { + category: 'Designing', + editor: 'Canva', + language: 'Canva Design', + project: ogTitle, + }); + } +}; + +function debounce(func: () => void, timeout = twoMinutes) { + let timer: NodeJS.Timeout | undefined; + return () => { + if (timer) { + return; + } + func(); + timer = setTimeout(() => { + clearTimeout(timer); + timer = undefined; + }, timeout); + }; +} + +document.body.addEventListener( + 'click', + debounce(() => init()), + true, +); + +document.body.addEventListener( + 'keypress', + debounce(() => init()), + true, +); diff --git a/webpack.config.ts b/webpack.config.ts index 6d6d3d0..42f180d 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -25,6 +25,7 @@ const getConfigByBrowser = (isProd: boolean, browser: BrowserTypes): webpack.Con devtools: [join(srcFolder, 'devtools.ts')], options: [join(srcFolder, 'options.tsx')], popup: [join(srcFolder, 'popup.tsx')], + wakatimeScript: [join(srcFolder, 'wakatimeScript.ts')], }, // mode: isProd ? 'production' : 'development', module: { diff --git a/xclap.ts b/xclap.ts index cb8bc47..1ce4796 100644 --- a/xclap.ts +++ b/xclap.ts @@ -30,6 +30,7 @@ const filesNeededForNextBuild = [ 'public/js/browser-polyfill.min.js', 'public/css/app.css', 'graphics/wakatime-logo-16.png', + 'wakatimeScript.js', ]; const chromeNextBuildFileWaitTask = waitForFilesTask( nextBuildFolder,