diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..eaf98f7 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,38 @@ +module.exports = { + root: true, + env: { + node: true + }, + extends: [ + "plugin:vue/vue3-essential", + "eslint:recommended", + "@vue/typescript/recommended" + ], + parserOptions: { + ecmaVersion: 2020 + }, + rules: { + "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", + "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { "argsIgnorePattern": "^_" } + ] + }, + overrides: [ + { + files: [ + "**/__tests__/*.{j,t}s?(x)", + "**/tests/unit/**/*.spec.{j,t}s?(x)", + "*.js" + ], + rules: { + "@typescript-eslint/no-var-requires": "off", + }, + env: { + jest: true + } + } + ] +}; diff --git a/.gitignore b/.gitignore index 75d5ca4..20128fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .DS_Store -/node_modules +node_modules /dist /crate/target/* /.log @@ -28,3 +28,10 @@ pnpm-debug.log* #Electron-builder output /dist_electron + +#Electron-builder config +electron-builder.yml + +#Window and session states +session.json +window.json diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 936e781..0000000 --- a/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@crimata:registry=https://gitlab.com/api/v4/projects/28849281/packages/npm/ -//gitlab.com/api/v4/projects/28849281/packages/npm/:_authToken=${NPM_TOKEN} \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..e955840 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..d7f13cc --- /dev/null +++ b/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', + transform: { + '^.+\\.vue$': 'vue-jest' + } +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..5df65d0 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,6 @@ +{ + "typeAcquisition": { + + "enable": true + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 82bd044..0000000 --- a/package-lock.json +++ /dev/null @@ -1,2213 +0,0 @@ -{ - "name": "Crimata", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "Crimata", - "version": "1.0.0", - "dependencies": { - "@crimata/nodeaudio": "^0.0.0", - "axios": "^0.21.1", - "base64-arraybuffer": "^1.0.1", - "electron-store": "^8.0.0", - "ws": "^7.3.1" - }, - "devDependencies": { - "electron": "^13.2.3" - } - }, - "node_modules/@crimata/nodeaudio": { - "version": "0.0.0", - "resolved": "https://gitlab.com/api/v4/projects/28849281/packages/npm/@crimata/nodeaudio/-/@crimata/nodeaudio-0.0.0.tgz", - "integrity": "sha1-KTpDGuL8j3x72BLNb4Iyg8zgARo=", - "hasInstallScript": true, - "dependencies": { - "bindings": "~1.2.1", - "sleep": "^6.3.0" - } - }, - "node_modules/@electron/get": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz", - "integrity": "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^9.6.0", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "engines": { - "node": ">=8.6" - }, - "optionalDependencies": { - "global-agent": "^2.0.2", - "global-tunnel-ng": "^2.7.1" - } - }, - "node_modules/@electron/get/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/@electron/get/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/get/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@electron/get/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@types/node": { - "version": "14.17.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.12.tgz", - "integrity": "sha512-vhUqgjJR1qxwTWV5Ps5txuy2XMdf7Fw+OrdChRboy8BmWUPkckOhphaohzFG6b8DW7CrxaBMdrdJ47SYFq1okw==", - "dev": true - }, - "node_modules/ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/atomically": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "dependencies": { - "follow-redirects": "^1.10.0" - } - }, - "node_modules/base64-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", - "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/bindings": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", - "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" - }, - "node_modules/boolean": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", - "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==", - "dev": true, - "optional": true - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/conf": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/conf/-/conf-10.0.2.tgz", - "integrity": "sha512-iyy4ArqyQ/yrzNASNBN+jaylu53JRuq0ztvL6KAWYHj4iN56BVuhy2SrzEEHBodNbacZr2Pd/4nWhoAwc66T1g==", - "dependencies": { - "ajv": "^8.1.0", - "ajv-formats": "^2.0.2", - "atomically": "^1.7.0", - "debounce-fn": "^4.0.0", - "dot-prop": "^6.0.1", - "env-paths": "^2.2.1", - "json-schema-typed": "^7.0.3", - "onetime": "^5.1.2", - "pkg-up": "^3.1.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dev": true, - "optional": true, - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/core-js": { - "version": "3.16.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.3.tgz", - "integrity": "sha512-lM3GftxzHNtPNUJg0v4pC2RC6puwMd6VZA7vXUczi+SKmCWSf4JwO89VJGMqbzmB7jlK7B5hr3S64PqwFL49cA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "node_modules/debounce-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", - "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", - "dependencies": { - "mimic-fn": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "optional": true, - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "optional": true - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "node_modules/electron": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-13.2.3.tgz", - "integrity": "sha512-FzWhbKHjq7ZTpPQFaYiLPL64erC8/BOsu5NlNN9nQ6f7rIP8ygKlNAlQit3vbOcksQAwItDUCIw4sW0mcaRpCA==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@electron/get": "^1.0.1", - "@types/node": "^14.6.2", - "extract-zip": "^1.0.3" - }, - "bin": { - "electron": "cli.js" - }, - "engines": { - "node": ">= 8.6" - } - }, - "node_modules/electron-store": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.0.tgz", - "integrity": "sha512-ZgRPUZkfrrjWSqxZeaxu7lEvmYf6tgl49dLMqxXGnEmliSiwv3u4rJPG+mH3fBQP9PBqgSh4TCuxHZImMMUgWg==", - "dependencies": { - "conf": "^10.0.0", - "type-fest": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true, - "optional": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "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, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "optional": true - }, - "node_modules/extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "dependencies": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - } - }, - "node_modules/extract-zip/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/extract-zip/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==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/extract-zip/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "dependencies": { - "pend": "~1.2.0" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/follow-redirects": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.2.tgz", - "integrity": "sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-agent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", - "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", - "dev": true, - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "core-js": "^3.6.5", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/global-tunnel-ng": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", - "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", - "dev": true, - "optional": true, - "dependencies": { - "encodeurl": "^1.0.2", - "lodash": "^4.17.10", - "npm-conf": "^1.1.3", - "tunnel": "^0.0.6" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", - "dev": true, - "optional": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "optional": true - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/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 - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-schema-typed": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", - "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "optional": true - }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "optional": true, - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/matcher/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-fn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "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==", - "dev": true - }, - "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm-conf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", - "dev": true, - "optional": true, - "dependencies": { - "config-chain": "^1.1.11", - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/onetime/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/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, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "engines": { - "node": ">=4" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true, - "optional": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "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==", - "dev": 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/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "optional": true, - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "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==", - "dev": true - }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true, - "optional": true - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "optional": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sleep": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz", - "integrity": "sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ==", - "hasInstallScript": true, - "dependencies": { - "nan": "^2.14.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "dev": true, - "dependencies": { - "debug": "^4.1.0" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, - "node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/ws": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.4.tgz", - "integrity": "sha512-zP9z6GXm6zC27YtspwH99T3qTG7bBFv2VIkeHstMLrLlDJuzA7tQ5ls3OJ1hOGGCzTQPniNJoHXIAOS0Jljohg==", - "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/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - }, - "dependencies": { - "@crimata/nodeaudio": { - "version": "0.0.0", - "resolved": "https://gitlab.com/api/v4/projects/28849281/packages/npm/@crimata/nodeaudio/-/@crimata/nodeaudio-0.0.0.tgz", - "integrity": "sha1-KTpDGuL8j3x72BLNb4Iyg8zgARo=", - "requires": { - "bindings": "~1.2.1", - "sleep": "^6.3.0" - } - }, - "@electron/get": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz", - "integrity": "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "global-agent": "^2.0.2", - "global-tunnel-ng": "^2.7.1", - "got": "^9.6.0", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "dependencies": { - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - } - } - }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/node": { - "version": "14.17.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.12.tgz", - "integrity": "sha512-vhUqgjJR1qxwTWV5Ps5txuy2XMdf7Fw+OrdChRboy8BmWUPkckOhphaohzFG6b8DW7CrxaBMdrdJ47SYFq1okw==", - "dev": true - }, - "ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - } - }, - "atomically": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" - }, - "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", - "requires": { - "follow-redirects": "^1.10.0" - } - }, - "base64-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz", - "integrity": "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==" - }, - "bindings": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", - "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" - }, - "boolean": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", - "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==", - "dev": true, - "optional": true - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } - } - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "conf": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/conf/-/conf-10.0.2.tgz", - "integrity": "sha512-iyy4ArqyQ/yrzNASNBN+jaylu53JRuq0ztvL6KAWYHj4iN56BVuhy2SrzEEHBodNbacZr2Pd/4nWhoAwc66T1g==", - "requires": { - "ajv": "^8.1.0", - "ajv-formats": "^2.0.2", - "atomically": "^1.7.0", - "debounce-fn": "^4.0.0", - "dot-prop": "^6.0.1", - "env-paths": "^2.2.1", - "json-schema-typed": "^7.0.3", - "onetime": "^5.1.2", - "pkg-up": "^3.1.0", - "semver": "^7.3.5" - } - }, - "config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dev": true, - "optional": true, - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "core-js": { - "version": "3.16.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.3.tgz", - "integrity": "sha512-lM3GftxzHNtPNUJg0v4pC2RC6puwMd6VZA7vXUczi+SKmCWSf4JwO89VJGMqbzmB7jlK7B5hr3S64PqwFL49cA==", - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "debounce-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", - "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", - "requires": { - "mimic-fn": "^3.0.0" - } - }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "optional": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "optional": true - }, - "dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "electron": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/electron/-/electron-13.2.3.tgz", - "integrity": "sha512-FzWhbKHjq7ZTpPQFaYiLPL64erC8/BOsu5NlNN9nQ6f7rIP8ygKlNAlQit3vbOcksQAwItDUCIw4sW0mcaRpCA==", - "dev": true, - "requires": { - "@electron/get": "^1.0.1", - "@types/node": "^14.6.2", - "extract-zip": "^1.0.3" - } - }, - "electron-store": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.0.0.tgz", - "integrity": "sha512-ZgRPUZkfrrjWSqxZeaxu7lEvmYf6tgl49dLMqxXGnEmliSiwv3u4rJPG+mH3fBQP9PBqgSh4TCuxHZImMMUgWg==", - "requires": { - "conf": "^10.0.0", - "type-fest": "^1.0.2" - } - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true, - "optional": true - }, - "end-of-stream": { - "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, - "requires": { - "once": "^1.4.0" - } - }, - "env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "optional": true - }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "follow-redirects": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.2.tgz", - "integrity": "sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA==" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "global-agent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz", - "integrity": "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==", - "dev": true, - "optional": true, - "requires": { - "boolean": "^3.0.1", - "core-js": "^3.6.5", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - } - }, - "global-tunnel-ng": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", - "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", - "dev": true, - "optional": true, - "requires": { - "encodeurl": "^1.0.2", - "lodash": "^4.17.10", - "npm-conf": "^1.1.3", - "tunnel": "^0.0.6" - } - }, - "globalthis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", - "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", - "dev": true, - "optional": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "optional": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "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": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "json-schema-typed": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", - "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "optional": true - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "optional": true, - "requires": { - "escape-string-regexp": "^4.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "optional": true - } - } - }, - "mimic-fn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==" - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" - }, - "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "dev": true - }, - "npm-conf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", - "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", - "dev": true, - "optional": true, - "requires": { - "config-chain": "^1.1.11", - "pify": "^3.0.0" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - } - } - }, - "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-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true, - "optional": true - }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "requires": { - "find-up": "^3.0.0" - } - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", - "dev": true, - "optional": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "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==", - "dev": 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" - } - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } - }, - "roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "optional": true, - "requires": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true, - "optional": true - }, - "serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "optional": true, - "requires": { - "type-fest": "^0.13.1" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "optional": true - } - } - }, - "sleep": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz", - "integrity": "sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ==", - "requires": { - "nan": "^2.14.1" - } - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true, - "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==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "dev": true, - "requires": { - "debug": "^4.1.0" - } - }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, - "optional": true - }, - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.4.tgz", - "integrity": "sha512-zP9z6GXm6zC27YtspwH99T3qTG7bBFv2VIkeHstMLrLlDJuzA7tQ5ls3OJ1hOGGCzTQPniNJoHXIAOS0Jljohg==", - "requires": {} - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - } - } -} diff --git a/package.json b/package.json index 31bd08d..24d13b1 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,104 @@ { "name": "Crimata", - "productName": "crimata-messenger", - "version": "1.0.0", + "version": "0.9.9", "private": true, - "description": "Cross-platform messenger app (Electron, Vue3, and TS)", + "description": "Cross-platform messenger application built with electron, vue3, and TS.", "author": { - "name": "Andrew Gundersen" + "name": "Enrique Hernandez" }, - "main": "src/main.js", "scripts": { - "start": "electron ." + "build": "vue-cli-service electron:build", + "dev": "vue-cli-service electron:serve", + "postinstall": "electron-builder install-app-deps", + "postuninstall": "electron-builder install-app-deps" }, + "main": "init.js", "dependencies": { - "@crimata/nodeaudio": "^0.0.0", + "@google-cloud/speech": "^4.2.0", + "@types/animejs": "^3.1.2", + "@types/bindings": "^1.3.0", + "@types/dom-mediacapture-record": "^1.0.7", + "@types/node": "^14.14.25", + "@types/uuid": "^8.3.0", + "@types/ws": "^7.2.7", + "animejs": "^3.2.0", "axios": "^0.21.1", - "base64-arraybuffer": "^1.0.1", + "core-js": "^3.6.5", + "electron-is-dev": "^2.0.0", "electron-store": "^8.0.0", + "electron-updater": "^4.3.8", + "mitt": "^2.1.0", + "naudiodon": "^2.3.2", + "node-record-lpcm16": "^1.0.1", + "update-electron-app": "^2.0.1", + "uuid": "^8.3.2", + "vue": "^3.0.0-0", + "vue-router": "^4.0.0-0", + "vuex": "^4.0.0-0", "ws": "^7.3.1" }, "devDependencies": { - "electron": "^13.2.3" + "@types/axios": "^0.14.0", + "@types/electron-devtools-installer": "^2.2.0", + "@types/jest": "^24.0.19", + "@typescript-eslint/eslint-plugin": "^2.33.0", + "@typescript-eslint/parser": "^2.33.0", + "@vue/cli-plugin-babel": "~4.5.0", + "@vue/cli-plugin-eslint": "~4.5.0", + "@vue/cli-plugin-router": "~4.5.0", + "@vue/cli-plugin-typescript": "~4.5.0", + "@vue/cli-plugin-unit-jest": "~4.5.0", + "@vue/cli-plugin-vuex": "~4.5.0", + "@vue/cli-service": "~4.5.0", + "@vue/compiler-sfc": "^3.0.0-0", + "@vue/eslint-config-typescript": "^5.0.2", + "@vue/test-utils": "^2.0.0-0", + "@wasm-tool/wasm-pack-plugin": "^1.3.1", + "electron": "^9.0.0", + "electron-devtools-installer": "^3.1.0", + "electron-log": "^4.3.4", + "eslint": "^6.7.2", + "eslint-plugin-vue": "^7.0.0-0", + "lint-staged": "^9.5.0", + "node-sass": "^4.12.0", + "optimize-wasm-webpack-plugin": "^1.0.12", + "sass-loader": "^8.0.2", + "spectron": "11.0.0", + "typescript": "~3.9.3", + "vue-cli-plugin-electron-builder": "~2.0.0-rc.6", + "vue-jest": "^5.0.0-0" + }, + "vue": { + "lintOnSave": false, + "pluginOptions": { + "electronBuilder": { + "mainProcessFile": "./src/init.ts", + "rendererProcessFile": "./src/render/main.ts", + "preload": "./src/render/preload.ts", + "builderOptions": { + "appId": "com.crimata.ElectronUpdaterApp", + "artifactName": "${productName}-${version}.${ext}", + "publish": { + "provider": "generic", + "url": "https://gitlab.com/api/v4/projects/25637892/jobs/artifacts/main/raw/dist_electron?job=build" + } + } + } + } + }, + "gitHooks": { + "pre-commit": "lint-staged" + }, + "lint-staged": { + "*.{js,jsx,vue,ts,tsx}": [ + "vue-cli-service lint", + "git add" + ] + }, + "productName": "crimata-messenger", + "repository": { + "type": "git", + "url": "https://gitlab.com/crimata/electron-app.git", + "release": "latest" } } diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..8f79d27 --- /dev/null +++ b/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + <%= htmlWebpackPlugin.options.title %> + + + +
+ + + diff --git a/src/account.js b/src/account.js deleted file mode 100644 index 239d89d..0000000 --- a/src/account.js +++ /dev/null @@ -1,44 +0,0 @@ -const { ipcMain } = require("electron"); - -const store = require("./utils/store"); -const { postLogin } = require("./api"); -const { ipcEmit } = require("./utils/emitter"); -const { launchSession, endSession } = require("./session"); - -// store.set("account", "smith12@gmail.com"); -store.set("account", "adgundersen@gmail.com"); - -async function login(_e, email, password) -{ - const res = await postLogin(email, password); - if (res) - { - const account = res.crimataId; - store.set("account", account); - ipcEmit("set-account", account); - launchSession(account); - return true; - } - else - { - return false; - } -}; - -function logout() -{ - store.delete("account"); - ipcEmit("set-account", false); - endSession(); -}; - -function initAccount() -{ - ipcMain.handle("login", login); - ipcMain.on("logout", logout); - - const account = store.get("account"); - if (account) launchSession(account); -} - -exports.initAccount = initAccount; diff --git a/src/account.ts b/src/account.ts new file mode 100644 index 0000000..581da0e --- /dev/null +++ b/src/account.ts @@ -0,0 +1,95 @@ + +import { postAuth, postLogin, postLogout } from "@/api/account"; +import { endSession, launchSession } from "@/session"; +import { getToken, setToken, setProfile, getProfile, clearStore } from "./store"; +import { parseAuthRes } from "./auth"; +import { ipcEmit } from "@/composables/useEmitter"; + +export const accountAuth = async (): Promise => { + + /* attempt to get a login token from the store */ + const token = getToken(); + + /* try to login with it, returns platform secret and new token on success */ + if (token) { + try { + + const res = await postAuth(token); + + const parsed = parseAuthRes(res); + + setToken(parsed.token) + setProfile(parsed.profile); + + return { + profile: parsed.profile, + token: parsed.token + }; + + } catch(e) { + console.log('[ACCOUNT]', e); + clearStore(); + throw(new Error('Failed to authenticate.')); + + } + } else { + throw(new Error('Unable to authenticate.')); + } +}; + +export const accountLogin: IpcHandlerCallback = async (payload) => { + const account = payload as AccountCredentials; + try { + + // attempt login with email password + const res = await postLogin(account.email, account.password); + const parsed = parseAuthRes(res); + + // save jwt token and profile + setToken(parsed.token); + setProfile(parsed.profile); + + // launch session + launchSession(parsed.token); + + // return profile to renderer + return parsed.profile; + + } catch(e) { + clearStore(); + throw e; + } +} + + +export const accountLogout = async (): Promise => { + + try { + // post logout to backend + await postLogout(); + + // remove key and crimataId + clearStore(); + + // kill crimata platform session + endSession(); + + return; + + } catch(e) { + console.log('[ACCOUNT]', e); + return (new Error('Failed to logout. Please try again.')); + } + +} + +export const updateAppState = (): void => { + + const profile = getProfile(); + + ipcEmit("set-profile", profile); + + // ipcEmit('messages') etc + +} + diff --git a/src/api.js b/src/api.js deleted file mode 100644 index 89d617e..0000000 --- a/src/api.js +++ /dev/null @@ -1,19 +0,0 @@ -const axios = require('axios').default; - -const config = require("./config"); - -const baseUrl = config.BUSINESS_URL + config.BUSINESS_PREFIX; - -async function postLogin(email, password) -{ - try { - const res = await axios.post(baseUrl + "/account/login", { - email: email, password: password - }); - return res.data; - } catch (e) { - return false; - } -} - -exports.postLogin = postLogin; \ No newline at end of file diff --git a/src/api/account.ts b/src/api/account.ts new file mode 100644 index 0000000..45baca2 --- /dev/null +++ b/src/api/account.ts @@ -0,0 +1,31 @@ + +import useHttp from "@/composables/useHttp"; +import axios from "axios"; +import {config} from "@/config"; + +const { post } = useHttp(); + +export const postAuth = async (token: string) => ( + await axios({ + url: config.BUSINESS_URL + config.BUSINESS_PREFIX + '/account/authenticate', + headers: { + Cookie: `crimataCookie=${token}` + }, + method: 'POST', + }) +); + + +export const postLogin = async (email: string, password: string) => ( + await post('/account/login', { email, password }) +); + + +export const postLogout = + async (): Promise => (await post('/account/logout')); + + + + + + diff --git a/src/assets/icon.icns b/src/assets/icon.icns deleted file mode 100644 index b173533..0000000 Binary files a/src/assets/icon.icns and /dev/null differ diff --git a/src/assets/tray/000.png b/src/assets/tray/000.png deleted file mode 100644 index e6d5d32..0000000 Binary files a/src/assets/tray/000.png and /dev/null differ diff --git a/src/assets/tray/000@2x.png b/src/assets/tray/000@2x.png deleted file mode 100644 index 6d61d7f..0000000 Binary files a/src/assets/tray/000@2x.png and /dev/null differ diff --git a/src/assets/tray/001.png b/src/assets/tray/001.png deleted file mode 100644 index 8d65b38..0000000 Binary files a/src/assets/tray/001.png and /dev/null differ diff --git a/src/assets/tray/001@2x.png b/src/assets/tray/001@2x.png deleted file mode 100644 index b7f0e2c..0000000 Binary files a/src/assets/tray/001@2x.png and /dev/null differ diff --git a/src/assets/tray/010.png b/src/assets/tray/010.png deleted file mode 100644 index bc5718c..0000000 Binary files a/src/assets/tray/010.png and /dev/null differ diff --git a/src/assets/tray/010@2x.png b/src/assets/tray/010@2x.png deleted file mode 100644 index 6b97193..0000000 Binary files a/src/assets/tray/010@2x.png and /dev/null differ diff --git a/src/assets/tray/011.png b/src/assets/tray/011.png deleted file mode 100644 index c2f8612..0000000 Binary files a/src/assets/tray/011.png and /dev/null differ diff --git a/src/assets/tray/011@2x.png b/src/assets/tray/011@2x.png deleted file mode 100644 index befcd5f..0000000 Binary files a/src/assets/tray/011@2x.png and /dev/null differ diff --git a/src/assets/tray/100.png b/src/assets/tray/100.png deleted file mode 100644 index 99df7df..0000000 Binary files a/src/assets/tray/100.png and /dev/null differ diff --git a/src/assets/tray/100@2x.png b/src/assets/tray/100@2x.png deleted file mode 100644 index 2d26144..0000000 Binary files a/src/assets/tray/100@2x.png and /dev/null differ diff --git a/src/assets/tray/101.png b/src/assets/tray/101.png deleted file mode 100644 index 523bc93..0000000 Binary files a/src/assets/tray/101.png and /dev/null differ diff --git a/src/assets/tray/101@2x.png b/src/assets/tray/101@2x.png deleted file mode 100644 index 91a15be..0000000 Binary files a/src/assets/tray/101@2x.png and /dev/null differ diff --git a/src/assets/tray/110.png b/src/assets/tray/110.png deleted file mode 100644 index ddd2524..0000000 Binary files a/src/assets/tray/110.png and /dev/null differ diff --git a/src/assets/tray/110@2x.png b/src/assets/tray/110@2x.png deleted file mode 100644 index fd073dd..0000000 Binary files a/src/assets/tray/110@2x.png and /dev/null differ diff --git a/src/assets/tray/111.png b/src/assets/tray/111.png deleted file mode 100644 index d2dd686..0000000 Binary files a/src/assets/tray/111.png and /dev/null differ diff --git a/src/assets/tray/111@2x.png b/src/assets/tray/111@2x.png deleted file mode 100644 index 2bc77fd..0000000 Binary files a/src/assets/tray/111@2x.png and /dev/null differ diff --git a/src/audio.js b/src/audio.js deleted file mode 100644 index 3736ee9..0000000 --- a/src/audio.js +++ /dev/null @@ -1,137 +0,0 @@ -const nodeAudio = require("@crimata/nodeaudio"); -const { globalShortcut, ipcMain } = require("electron"); - -const { sendMessage } = require("./io"); -const { updateTray } = require("./tray"); -const { encode, decode } = require("./codec"); -const { backgroundMitt, ipcEmit } = require("./utils/emitter"); - -let inputDevice; -let outputDevice; - -let setWriteId; -let setStreamsId; - -let autoStopId; /* keep track of recording time */ - -let playbackId; /* UID of the message being played */ - -const streamState = { rec: false, pb: false }; - -const /** @type {Int16Array[]} */ chunks = []; - -backgroundMitt.on("data", (int16Arr) => { - if (streamState.rec) chunks.push(int16Arr); -}); - -backgroundMitt.on("write", (int16Arr) => { - clearTimeout(setWriteId); - setWriteId = setTimeout(setPlaybackStatus.prototype.bind(null, false), 500); -}); - -function setStreams() -{ - const defaultInput = nodeAudio.core.GetDefaultInputDevice(); - const defaultOutput = nodeAudio.core.GetDefaultOutputDevice(); - - if (inputDevice !== defaultInput) - { - inputDevice = defaultInput; - nodeAudio.core.CloseInputStream(inputDevice); - nodeAudio.core.OpenInputStream(inputDevice); - } - - if (outputDevice !== defaultOutput) - { - outputDevice = defaultOutput; - nodeAudio.core.CloseOutputStream(outputDevice); - nodeAudio.core.OpenOutputStream(outputDevice); - } -} - -function startRecording() -{ - setRecordingStatus(true); - - /* 15s recording time limit */ - autoStopId = setTimeout(stopRecording, 15000); -} - -function stopRecording() -{ - if (autoStopId) - { - clearTimeout(autoStopId); - } - - setRecordingStatus(false); - - sendMessage({ - category: "audio/L16", - text: null, - blob: encode(nodeAudio.utils.mergeChunks(chunks).buffer) - }); - - chunks.length = 0; -} - -function initAudio() -{ - nodeAudio.core.Initialize(backgroundMitt.emit.bind(backgroundMitt)); - setStreamsId = setInterval(setStreams, 2000); - - const res = globalShortcut.register('CommandOrControl+Return', () => { - streamState.rec ? stopRecording() : startRecording(); - }); - - if (!res) throw new Error("Failed to register recording shortcut"); -} - -function playback(base64String, id) -{ - /* terminate any current playback */ - nodeAudio.core.CancelPlayback(); - ipcEmit("playback", playbackId, false); - - playbackId = id; - setPlaybackStatus(true); - nodeAudio.core.WriteToOutputStream(decode(base64String)); -} - -function terminateAudio() -{ - globalShortcut.unregisterAll(); - - /* Only terminate PA if initialized */ - if (setStreamsId) - { - clearInterval(setStreamsId); - nodeAudio.core.Terminate(); - } -} - -function setRecordingStatus(status) -{ - streamState.rec = status; - ipcEmit("record", status); - updateTray("recording", streamState.rec); -} - -function setPlaybackStatus(status) -{ - console.log("Setting playback status: ", status); - - streamState.pb = status; - ipcEmit("playback", playbackId, status); - updateTray("playback", status); -} - -exports.initAudio = initAudio; -exports.terminateAudio = terminateAudio; -exports.playback = playback; -exports.streamState = streamState; - - -// setWriteId = setTimeout(() => { -// setPlaybackStatus(false); -// }, 500); diff --git a/src/audio.ts b/src/audio.ts new file mode 100644 index 0000000..7ae2562 --- /dev/null +++ b/src/audio.ts @@ -0,0 +1,187 @@ +/* eslint @typescript-eslint/no-var-requires: "off" */ + +"use strict"; + +// where the audio goes +let buffer: ArrayBuffer[] = []; + +// place audio data in buffer +export const collect: IpcListenerCallback = (chunk) => { + if (chunk) buffer.push(chunk); +} + +// return audio and clear buffer +export const flush = async () => { + const bufferCopy = buffer; + buffer = []; + return bufferCopy; +} + +// import { backgroundMitt } from '@/modules/emitter'; +// const portAudio = require('naudiodon'); + +// // Audio in and out stream objects. +// let ai: typeof portAudio.AudioIO | boolean = false; +// let ao: typeof portAudio.AudioIO | boolean = false; + +// // Whether activly recording. +// let record = false; + +// const audioContainer = { +// input: '', +// } + +// const audioOptions = { +// channelCount: 1, +// sampleFormat: 16, +// sampleRate: 16000, +// deviceId: -1, +// closeOnError: false, +// } + +// export const toggleRecord = (): void => { record = !record }; + + +// export const fetchAudioInput = (): Promise => ( + +// new Promise((resolve, reject) => { + +// try { +// resolve(audioContainer.input); +// toggleRecord(); +// } catch (e) { +// reject(new Error('Failed to fetch the audio.')) +// } + +// }) +// ) + + +// // Main audio function run by run.ts module. +// export function initAudioIO(): void { +// console.log("AUDIO:Starting io streams.") + +// if (!ai) { + +// // Initialize and start input stream. +// ai = new portAudio.AudioIO({ inOptions: audioOptions }); +// ai.setEncoding("hex"); +// ai.start(); + +// // On each data chunk... +// ai.on('data', (chunk: string) => { + +// // If recording, we capture the data. +// if (record) { +// console.log('AUDIO:Recording...') +// audioContainer.input += chunk; +// } + +// // Else, we don't capture and also clear audioContainer. +// else { +// if (audioContainer.input.length) { +// audioContainer.input = ""; +// } +// } + +// }); +// } + +// if (!ao) { + +// // Initialize and start input stream. +// ao = new portAudio.AudioIO({ outOptions: audioOptions }); +// ao.start(); + +// } +// } + + +// // ---Audio playback-------------------------------------------- + +// // Split Buffer into an array of len-sized Buffers. +// function bufSplit(buf: Buffer, len: number): Array { +// const chunks = []; +// let i = 0; +// let L = len; + +// while(i < buf.byteLength) { +// chunks.push(buf.slice(i, L)); +// i = L; +// L += len; +// } + +// return chunks; +// } + +// // Audio playback. +// export function play(input: string): void { + +// // Format the audio. +// const audio = bufSplit( +// Buffer.from(input as string, 'hex'), +// 8192 +// ); + +// // Called on end of write. +// const callback = () => { + +// // We stop audio playback anim. +// backgroundMitt.emit('ipc-renderer', { +// endpoint: 'stop-playback-anim' +// }); + +// } + +// write(); + +// // Iterate through audio array and write buffers to portAudio writable. +// function write() { +// let chunk: Buffer; +// let ok = true; +// let i = 0; + +// do { +// chunk = audio[i]; +// if (i === audio.length - 1) { +// // write last chunk. +// ao.write(chunk, null, callback); +// } else { +// // check for backpreassure. +// ok = ao.write(chunk, null); +// } +// i++; +// } while (i < audio.length && ok); + +// if (i < audio.length) { +// // Had to stop early! +// // Write some more once it drains. +// ao.once('drain', write); +// } +// } +// } + +// // ------------------------------------------------------------- + +// // Get's called on window close. +// export async function stopStream() { +// console.log("AUDIO:Stopping audio stream.") +// if (ai) { +// try { +// await ai.quit() +// } catch(e){ +// console.log('AUDIO: Failed to shutdown audio input.'); +// throw e; +// } +// } +// if (ao) { +// try { +// await ao.quit() +// } catch(e){ +// console.log('AUDIO: Failed to shutdown audio output.'); +// throw e; +// } +// } +// } + + diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 0000000..14ed36a --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,13 @@ + +export const parseAuthRes = (authRes: any) => { + const token = authRes.headers['set-cookie'][0].split(";")[0].split("=")[1] as string; + const profile = authRes.data as Profile; + return { + token, + profile + } +}; + + + + diff --git a/src/codec.js b/src/codec.js deleted file mode 100644 index 3a9a8bf..0000000 --- a/src/codec.js +++ /dev/null @@ -1,13 +0,0 @@ -const { ipcMain } = require("electron"); -const { encode, decode } = require("base64-arraybuffer"); - -ipcMain.handle("encode", (_e, arrayBuffer) => { - return encode(arrayBuffer); -}); - -ipcMain.handle("decode", (_e, b64string) => { - return decode(b64string); -}); - -exports.encode = encode; -exports.decode = decode; \ No newline at end of file diff --git a/src/composables/autoUpdate.ts b/src/composables/autoUpdate.ts new file mode 100644 index 0000000..1a6b780 --- /dev/null +++ b/src/composables/autoUpdate.ts @@ -0,0 +1,31 @@ +const { autoUpdater } = require('electron-updater'); + +let win: boolean; + +// Listen for window creation. +backgroundMitt.on('window-active', (state: boolean) => { + win = state; +}); + +// Auto updating. +autoUpdater.requestHeaders = { 'PRIVATE-TOKEN': 'mvvgWYwWnot4bisiQMh_' } + +autoUpdater.on('update-available', (info: any) => { + console.log(`Update available: ${info.version}`) +}) + +autoUpdater.on('update-downloaded', (info: any) => { + + const updateDialog = { + type: 'info', + buttons: ['Restart', 'Later'], + title: 'Application Update', + message: info.version, + detail: 'A new version has been downloaded. Restart the application to apply the updates.' + } + + dialog.showMessageBox(updateDialog).then((returnValue) => { + if (returnValue.response === 0) autoUpdater.quitAndInstall() + }) + +}) \ No newline at end of file diff --git a/src/composables/useEmitter.ts b/src/composables/useEmitter.ts new file mode 100644 index 0000000..f95f328 --- /dev/null +++ b/src/composables/useEmitter.ts @@ -0,0 +1,16 @@ + +// Backend emitter +// +const EventEmitter = require('events'); +class BackgroundMitt extends EventEmitter { } + +export const backgroundMitt = new BackgroundMitt(); + +export const ipcEmit = (channel: string, payload: T) => { + backgroundMitt.emit('ipc-renderer', { + channel, + payload + }); +}; + + diff --git a/src/composables/useHttp.ts b/src/composables/useHttp.ts new file mode 100644 index 0000000..6b9ee56 --- /dev/null +++ b/src/composables/useHttp.ts @@ -0,0 +1,51 @@ + +import axios, { AxiosRequestConfig } from 'axios'; +import {config} from "@/config"; + +const baseURL = config.BUSINESS_URL + config.BUSINESS_PREFIX; + +interface Request { + endpoint: string; + query?: Record; + config?: Record; +} + +const makeQuery = (reqQuery: Record) => { + + let result = ''; + + result = '?' + Object.entries(reqQuery) + .map(([ key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) + .join('&') + + return result; +}; + + +export default function useHttp() { + + const api = axios.create({ + baseURL, + withCredentials: true, + }); + + + const post = async (endpoint: string, payload?: Record): Promise => ( + await api.post(endpoint, payload) + ) + + + const get = async (req: Request) => { + + if (req.query) { + req.endpoint += makeQuery(req.query); + } + + const res = await api.get(req.endpoint, req.config); + return res; + }; + + return { + get, post + } +} diff --git a/src/composables/useIpcMain.ts b/src/composables/useIpcMain.ts new file mode 100644 index 0000000..ebfc4bc --- /dev/null +++ b/src/composables/useIpcMain.ts @@ -0,0 +1,82 @@ + +import { ipcMain, IpcMainInvokeEvent, IpcMainEvent } from "electron"; + +export class IpcHandler implements IIpcHandler { + + readonly channel: string; + + readonly _handlerCallback: IpcHandlerCallback; + + constructor(options: { + channel: string; + handlerCallback: IpcHandlerCallback; + }) { + this.channel = options.channel; + this._handlerCallback = options.handlerCallback; + } + + handle() { + this.remove(); + ipcMain.handle(this.channel, this._onInvoke); + } + + remove() { + ipcMain.removeHandler(this.channel); + } + + private _onInvoke = (_e: IpcMainInvokeEvent, payload?: string | null): Promise => { + + return new Promise(async (resolve, reject) => { + + console.log(`[IPC] Handle:${this.channel}`); + + try { + + const params = payload ? JSON.parse(payload) : null; + + const res = await this._handlerCallback(params); + + resolve(res as unknown as ReturnType); + + } catch(e) { + console.log(`[IPC] Error:${this.channel}`); + reject(e); + } + }); + } + +} + + +export class IpcListener implements IIpcListener { + + readonly channel: string; + + readonly _listenerCallback: IpcListenerCallback; + + constructor(options: { + channel: string; + listenerCallback: IpcListenerCallback; + }) { + this.channel = options.channel; + this._listenerCallback = options.listenerCallback; + } + + listen() { + this.remove(); + ipcMain.on(this.channel, this._onPost); + } + + remove() { + ipcMain.removeAllListeners(this.channel); + } + + private _onPost = (_e: IpcMainEvent, payload?: string | null): void => { + + console.log(`[IPC] Post: ${this.channel}`); + + const params = payload ? JSON.parse(payload) : null; + this._listenerCallback(params); + } + +} diff --git a/src/composables/useMessageCanvas.ts b/src/composables/useMessageCanvas.ts new file mode 100644 index 0000000..c85f7ed --- /dev/null +++ b/src/composables/useMessageCanvas.ts @@ -0,0 +1,36 @@ + + + +export default class Canvas { + + messages: Message[]; + + /* seed canvas with messages on init */ + constructor(messages: Message[]) { + this.messages = messages; + ipcEmit("seed-view", this.messages); + } + + /* add a new message to the canvas */ + add(message: Message) { + this.messages.push(message); + ipcEmit("update-view", message); + } + + /* update an existing message */ + update(message: Message) { + + /* get the target message */ + let target_message = this.messages.filter((m: Message) => { + return m.uid = message.uid; + })[0]; + + /* replace the target message */ + if (target_message) { + target_message = message; + ipcEmit("update-view", message); + } + + } + +} diff --git a/src/composables/useSaveToJSON.ts b/src/composables/useSaveToJSON.ts new file mode 100644 index 0000000..06ab4b9 --- /dev/null +++ b/src/composables/useSaveToJSON.ts @@ -0,0 +1,13 @@ + +import {config} from "@/config"; +import fs from 'fs'; + +export const saveToJson = (fileName: string, data: any) => { + + fs.writeFile(config.configPath + fileName, JSON.stringify(data), (err) => { + if (err) { + console.log("Error when saving to json.") + } + }) + +} diff --git a/src/composables/useWebsockets.ts b/src/composables/useWebsockets.ts new file mode 100644 index 0000000..d1cfc02 --- /dev/null +++ b/src/composables/useWebsockets.ts @@ -0,0 +1,89 @@ + +"use strict"; + +import WebSocket from 'ws'; + + +const _connectionCheckTimeout = 4000; +const _reconnectTimeout = 1000; +let _connectionCheckInterval: ReturnType; + + +export default function useWebSockets( + messageCallback: (message: string) => void, + connectionStatusCallback: (alive: boolean) => void, +) { + + let socket: WebSocket; + + const send = async (data: Record): Promise => { + return new Promise((resolve, reject) => { + if (socket) { + if (socket.readyState === WebSocket.OPEN) { + socket.send(JSON.stringify(data)); + resolve(true); + } + } + reject(false); + }); + } + + const connect = (socketUrl: string, secret: string) => { + + // avoid setting multiple interval; + if (_connectionCheckInterval) clearInterval(_connectionCheckInterval); + + /* create a new socket */ + socket = new WebSocket(socketUrl); + + /* add event listeners */ + socket.on("open", () => { + + socket.send(JSON.stringify({key: secret})); + + // ping server + _connectionCheckInterval = setInterval(() => { + + socket.ping(null, true, (e: Error) => { + if (e) { + socket.close(); + connectionStatusCallback(false); + setTimeout(() => connect(socketUrl, secret), _reconnectTimeout); + } + }); + + }, _connectionCheckTimeout); + + }); + + socket.on("message", (event: WebSocket.MessageEvent) => { + console.log("message received", event); + messageCallback(event.toString()) + }); + + socket.on("close", (event: WebSocket.CloseEvent) => { + connectionStatusCallback(false); + clearInterval(_connectionCheckInterval); + if (!event.wasClean) { + setTimeout(() => connect(socketUrl, secret), _reconnectTimeout); + } + + }); + + socket.on("pong", () => connectionStatusCallback(true)); + + } + + const close = () => { + if (socket) { + socket.close(); + } + } + + return { + connect, + send, + close + }; + +} diff --git a/src/config.js b/src/config.ts similarity index 79% rename from src/config.js rename to src/config.ts index adf5e22..855b1b2 100644 --- a/src/config.js +++ b/src/config.ts @@ -1,4 +1,5 @@ -const { app } = require("electron"); + +import { app } from "electron"; const env = process.env; @@ -8,9 +9,9 @@ const PLATFORM_IP = env.PLATFORM_IP || 'http://127.0.0.1'; const BUSINESS_PORT = env.BUSINESS_PORT || 3000; const BUSINESS_IP = env.BUSINESS_IP || 'http://127.0.0.1'; -module.exports = { +export const config = { PLATFORM_URL: `${PLATFORM_IP}:${PLATFORM_PORT}`, BUSINESS_URL: `${BUSINESS_IP}:${BUSINESS_PORT}`, BUSINESS_PREFIX: '/api', - configPath: app.getPath('userData'), -} \ No newline at end of file + configPath: app.getPath('userData') +} diff --git a/src/init.ts b/src/init.ts new file mode 100644 index 0000000..9a0fdf6 --- /dev/null +++ b/src/init.ts @@ -0,0 +1,52 @@ +/** + * Entry point for Crimata electron app. + * "Look on my Works, ye Mighty, and despair!" + */ + +"use strict"; + +import { app, protocol } from "electron"; +import createWindow from "./window"; +import main from "./main"; +import { backgroundMitt } from '@/composables/useEmitter'; + +console.log('Starting Crimata electron app.'); + +// Scheme must be registered before the app is ready +protocol.registerSchemesAsPrivileged([ + { scheme: "app", privileges: { secure: true, standard: true } } +]); + +const isDev = require('electron-is-dev'); + +let win: boolean; + +// Listen for window creation. +backgroundMitt.on('window-active', (state: boolean) => { + win = state; +}); + +/* Start main process on ready */ +app.on("ready", async () => { + await main(); +}); + +// Must keep to ensure app doesn't quit on close. +app.on("before-quit", async () => { +}); + +// Must keep to ensure app doesn't quit on close. +app.on("window-all-closed", () => { +}); + +// When user clicks app icon (re-open) +app.on("activate", () => { + if (!win) createWindow(); +}); + +// Exit cleanly on request from parent process in development mode. +if (isDev) { + process.on("SIGTERM", () => { + app.quit(); + }); +} diff --git a/src/io.js b/src/io.js deleted file mode 100644 index a269d49..0000000 --- a/src/io.js +++ /dev/null @@ -1,80 +0,0 @@ -const WebSocket = require("ws"); -const { ipcMain } = require("electron"); - -const config = require("./config"); -const { updateTray } = require("./tray"); -const { backgroundMitt, ipcEmit } = require("./utils/emitter"); - -let connection = null; - -let pingId; -let reconnectId; - -function connectToPlatform(account) -{ - if (pingId) clearInterval(pingId); - - connection = new WebSocket(`${config.PLATFORM_URL}/${account}`) - - .on("open", () => pingId = setInterval(() => connection.ping(null, true), 1000)) - - .on("pong", () => setConnectionStatus(0)) - - .on("error", () => {}) /** keep silent on error */ - - .on("message", (payload) => backgroundMitt.emit("message", JSON.parse(payload))) - - .on("close", (code, reason) => { - setConnectionStatus(1); - if (reason !== "logout") { - reconnectId = setTimeout(() => connectToPlatform(account), 500); - } - }); -} - -function sendMessage(message) -{ - if (connection.readyState === WebSocket.OPEN) { - connection.send(JSON.stringify(message)); - } else console.log("Failed to send message"); -} - -function disconnectFromPlatform() -{ - clearTimeout(reconnectId); - connection.close(1000, "logout"); -} - -function setConnectionStatus(status) -{ - updateTray("disconnect", status); - ipcEmit("ws-status", status); -} - -ipcMain.on("message", (_e, message) => sendMessage(message)); - -exports.connectToPlatform = connectToPlatform; -exports.sendMessage = sendMessage; -exports.disconnectFromPlatform = disconnectFromPlatform; - - - -/* ------------- Test Messages------------- */ - - - -setTimeout(() => { - - // sendMessage({ - // category: "text/plain", - // text: "show contacts", - // blob: null - // }) - - sendMessage({ - category: "text/plain", - text: "add contact John Smith, john.smith97@gmail.com", - blob: null - }) - -}, 2000); diff --git a/src/ipc/handlers.ts b/src/ipc/handlers.ts new file mode 100644 index 0000000..5529662 --- /dev/null +++ b/src/ipc/handlers.ts @@ -0,0 +1,28 @@ + +"use strict"; + +import { accountLogin, accountLogout, accountProfile } from "@/account"; +import { IpcHandler } from "@/composables/useIpcMain"; +import { flush } from "@/audio"; + + +const LOGIN_CHANNEL = "invoke-account-login"; +const LOGOUT_CHANNEL = "invoke-account-logout"; +const GET_AUDIO_CHANNEL = "invoke-audio-flush"; + +export const loginHandler = new IpcHandler({ + channel: LOGIN_CHANNEL, + handlerCallback: accountLogin +}); + +export const logoutHandler = new IpcHandler({ + channel: LOGOUT_CHANNEL, + handlerCallback: accountLogout +}); + +export const getAudioHandler = new IpcHandler({ + channel: GET_AUDIO_CHANNEL, + handlerCallback: flush +}); + + diff --git a/src/ipc/index.ts b/src/ipc/index.ts new file mode 100644 index 0000000..e6eb90a --- /dev/null +++ b/src/ipc/index.ts @@ -0,0 +1,33 @@ + +"use strict"; + +import * as handlers from "./handlers"; +import * as listeners from "./listeners"; + +const ipcHandlers: IPCHandlers = {}; +const ipcListeners: IPCListeners = {}; + +const _initHandlers = (): void => { + for (const [key, handler] of Object.entries(handlers)) { + if (!(key in ipcHandlers)) { + ipcHandlers[key] = handler; + handler.handle(); + } + } +}; + +const _initListeners = (): void => { + for (const [key, listener] of Object.entries(listeners)) { + if (!(key in ipcListeners)) { + ipcListeners[key] = listener; + listener.listen(); + } + } +}; + +export default function initIpcMain(): void { + _initHandlers(); + _initListeners(); +} + + diff --git a/src/ipc/listeners.ts b/src/ipc/listeners.ts new file mode 100644 index 0000000..8ef5d45 --- /dev/null +++ b/src/ipc/listeners.ts @@ -0,0 +1,24 @@ + +import { IpcListener } from "@/composables/useIpcMain" +import { sendMessage } from '@/session'; +import { collect } from "@/audio"; +import { updateAppState } from "@/account"; + +const CLIENT_MESSAGE_CHANNEL = "post-session-send" +const GET_AUDIO_CHANNEL = "post-audio-collect"; +const APP_MOUNT_CHANNEL = "post-app-mount"; + +export const messageListener = new IpcListener({ + channel: CLIENT_MESSAGE_CHANNEL, + listenerCallback: sendMessage +}); + +export const audioChunkListener = new IpcListener({ + channel: GET_AUDIO_CHANNEL, + listenerCallback: collect +}); + +export const appMountListener = new IpcListener({ + channel: APP_MOUNT_CHANNEL, + listenerCallback: updateAppState +}); diff --git a/src/main.js b/src/main.js deleted file mode 100644 index 841579e..0000000 --- a/src/main.js +++ /dev/null @@ -1,29 +0,0 @@ -const { app, protocol, ipcMain, nativeTheme } = require("electron"); - -const store = require("./utils/store"); - -const { initTray } = require("./tray"); -const { postLogin } = require("./api"); -const { createWin } = require("./window"); -const { initAccount } = require("./account"); -const { terminateAudio } = require("./audio"); - -// Assert a light theme -nativeTheme.themeSource = "light"; - -app.on("ready", () => -{ - initTray(); - - createWin(); - - initAccount(); -}); - -// app.on("will-quit", terminateAudio); - -// When user clicks app icon (re-open) -app.on("activate", createWin); - -// Prevents app from quitting on window close event -app.on('window-all-closed', (e) => e.preventDefault()); \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..a360835 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,39 @@ +/** + * Where the background logic really begins, gets called by app.onReady(). + * + * Handles authentication. If profile is set, we launch a session, which consis + * of opening a connection with the platform, initializing the audio streams. + * + * The session is primarily an interface between the frontend and the platform, + * relaying messages from one to the other. + * + */ + +import initIpcMain from "@/ipc/index"; +import { accountAuth, updateAppState } from "./account"; +import { launchSession } from "./session"; +import createWindow from "./window"; + +let authState: AuthState | null; + +export default async function main() { + + /* initiate controls for frontend to use when needed */ + initIpcMain(); + + /* launch browser window */ + await createWindow(); + + try { + authState = await accountAuth() as AuthState; + } catch(e) { + console.log('AUTH:', e); + authState = null; + } finally { + if (authState) { + launchSession(authState.token as string); + } + updateAppState(); + } + +} diff --git a/src/preview.html b/src/preview.html deleted file mode 100644 index 8c8fc48..0000000 --- a/src/preview.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - -
- - - - - diff --git a/src/render/App.vue b/src/render/App.vue new file mode 100644 index 0000000..686591b --- /dev/null +++ b/src/render/App.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/src/render/assets/connectLogo.svg b/src/render/assets/connectLogo.svg deleted file mode 100644 index 7a390a4..0000000 --- a/src/render/assets/connectLogo.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/render/assets/settingsIcon.svg b/src/render/assets/settingsIcon.svg deleted file mode 100644 index 25399f5..0000000 --- a/src/render/assets/settingsIcon.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/render/components/app.js b/src/render/components/app.js deleted file mode 100644 index 08909a6..0000000 --- a/src/render/components/app.js +++ /dev/null @@ -1,42 +0,0 @@ -import Splash from "./splash.js"; -import Messenger from "./messenger.js"; -import Login from "./login.js"; - -const account = Vue.ref(null); - -const App = -{ - components: { - Splash, - Messenger, - Login - }, - - setup() - { - return { account }; - }, - - template: ` -
- -
- - ` -} - -window.mainApi.on("set-account", (account_) => { - console.log("setting-account"); - account.value = account_; -}); - -export default App; -export { account }; - -// Is called when window is about to close or reload -window.onbeforeunload = () => console.log("beforeUnload"); \ No newline at end of file diff --git a/src/render/components/bubble.js b/src/render/components/bubble.js deleted file mode 100644 index 16f7983..0000000 --- a/src/render/components/bubble.js +++ /dev/null @@ -1,36 +0,0 @@ -const Bubble = -{ - props: ["category", "text", "blob", "modifier", "child"], - - setup(props) - { - const getUrl = () => { - return `data:${props.category};base64,${props.blob}`; - } - - Vue.onMounted(() => { - document.getElementById("messenger").dispatchEvent(new CustomEvent('adjust-scroll')); - }); - - return { getUrl }; - }, - - template: ` -
- -

{{ text }}

- -

{{ text }}

- -
- - - - {{ text }} - -
` -}; - -export default Bubble; - -// TODO: should include other styling/actions for audio bubbles \ No newline at end of file diff --git a/src/render/components/bubble.vue b/src/render/components/bubble.vue new file mode 100644 index 0000000..982cef3 --- /dev/null +++ b/src/render/components/bubble.vue @@ -0,0 +1,331 @@ + + + + + + + diff --git a/src/render/components/context.js b/src/render/components/context.js deleted file mode 100644 index b6879ae..0000000 --- a/src/render/components/context.js +++ /dev/null @@ -1,38 +0,0 @@ - -const Context = -{ - props: ["modifier", "context", "avatar", "id"], - - setup(props) { - const playback = Vue.ref(false); - - Vue.onMounted(() => { - window.mainApi.on("playback", (id, status) => { - if (id === props.id) { - playback.value = status; - } - }); - }); - - return { playback }; - }, - - template: ` - - -
- - - -
- - {{ context }} - -
` -} - -export default Context; diff --git a/src/render/components/control/draggify.js b/src/render/components/control/draggify.js deleted file mode 100644 index f85b930..0000000 --- a/src/render/components/control/draggify.js +++ /dev/null @@ -1,198 +0,0 @@ -const saveLocation = "input_item_position"; -const defaultPosition = { x: 15, y: window.innerHeight - 300 }; - -//---Dragabble Helper Funcs-------------------------------------- - -// Calculate distance to nearest side. -function calcSideProximity (elementX, elementLength, winW) { - - // Calc right short. - let short = winW - elementX - elementLength; - - // See if it's left short. - if (elementX + 20 < winW / 2) { - short = elementX - } - - return short -} - -// Update elementX or elementY value on window resize -function calcPosition (elementPosition, elementLength, percent, short, win) { - - // Is it close to the right/bottom side? - if (percent > 0.75) { - elementPosition = win - short - elementLength; - } - - // Is it not close to a side? - if (percent < 0.75 && percent > 0.25) { - elementPosition = win * percent - } - - return elementPosition -} - -function draggify(elementId, margin) { - - let element; - - /* only compatible with elements having equal width and height */ - let elementLength; - - // Cords of inputItem. - const elementX = Vue.ref(); - const elementY = Vue.ref(); - - // Position of inputItem on terms of percentage of window. - let percentX; - let percentY; - - // How close inputItem is to closest X or Y side. - let xShort; - let yShort; - - // Keep track of window size; - let winW = window.innerWidth; - let winH = window.innerHeight; - - //---Reposition Anime----------------------------------------------- - - // Move element to target smoothly. - const repositionAnime = (xChange, yChange) => { - const xStep = xChange / 6000; - const yStep = yChange / 6000; - - for (let i = 1; i <= 6000; i++) { - - setTimeout(() => { - elementX.value += xStep; - elementY.value += yStep; - }, 16) // 60fps - - } - } - - //---Event Handlers----------------------------------------------- - - // Update the position of inputItem on mouse dragging. - const onMouseMove = (e) => { - e.preventDefault(); - - const inputItemRect = element.getBoundingClientRect(); - elementX.value = inputItemRect.left + e.movementX; - elementY.value = inputItemRect.top + e.movementY; - } - - // Add an event listener for dragging. - const onMouseDown = (e) => { - e.preventDefault(); - window.addEventListener('mousemove', onMouseMove); - window.addEventListener('mouseup', onMouseUp); - } - - // Update position references when user is done moving targetEl. - const onMouseUp = (e) => { - e.preventDefault(); - - window.removeEventListener('mousemove', onMouseMove); - window.removeEventListener('mouseup', onMouseUp); - - // See if and calculate reposition. - let x = 0; // vector change - let y = 0; - - if (elementX.value < 0) { - x = (elementX.value - margin)*-1 - } - - if (elementX.value > winW - elementLength) { - const b = winW - elementLength - margin; - x = (elementX.value - b)*-1 - } - - // Reposition y - if (elementY.value < 0) { - y = (elementY.value - margin)*-1 - } - - if (elementY.value > winH - elementLength) { - const d = winH - elementLength - margin; - y = (elementY.value - d)*-1 - } - - // Reposition if needed. - if (x !== 0 || y !== 0) repositionAnime(x, y); - - xShort = calcSideProximity(elementX.value, elementLength, winW); - yShort = calcSideProximity(elementY.value, elementLength, winH); - - // Update percentages. - percentX = elementX.value / winW; - percentY = elementY.value / winH; - - // Save position. - savePosition(); - } - - // Update position of targetEl on windowResize. - const onWindowResize = (_e) => { - - // Update window dimensions. - winW = window.innerWidth; - winH = window.innerHeight; - - elementX.value = calcPosition(elementX.value, elementLength, percentX, xShort, winW); - elementY.value = calcPosition(elementY.value, elementLength, percentY, yShort, winH); - - savePosition(); - } - - const savePosition = () => { - window.localStorage.setItem(saveLocation, JSON.stringify({ - x: elementX.value, - y: elementY.value - })); - } - - //--------------------------------------------------------------- - - Vue.onMounted(() => { - - element = document.getElementById(elementId); - - elementLength = element.offsetWidth; - - // Initialize the positional references. - percentX = elementX.value / window.innerWidth; - percentY = elementY.value / window.innerHeight; - - xShort = calcSideProximity(elementX.value, elementLength, window.innerWidth); - yShort = calcSideProximity(elementX.value, elementLength, window.innerHeight); - - // Then, we can listen for window resize (and mousedown). - element.addEventListener("mousedown", onMouseDown); - window.addEventListener('resize', onWindowResize) - }); - - // remove event listeners on component dismount. - Vue.onUnmounted(() => { - element.removeEventListener('mousedown', onMouseDown); - window.removeEventListener('resize', onWindowResize) - window.removeEventListener('mouseup', onMouseUp) - window.removeEventListener('mousemove', onMouseMove); - }); - - // Try loading initPosition, otherwise set default values - let initPosition; - const rawData = window.localStorage.getItem("saveLocation") - rawData ? initPosition = JSON.parse(rawData) : initPosition = defaultPosition; - - elementX.value = initPosition.x; - elementY.value = initPosition.y; - - return { elementX, elementY }; - -} - -export default draggify; diff --git a/src/render/components/control/scroll.js b/src/render/components/control/scroll.js deleted file mode 100644 index 4666ea1..0000000 --- a/src/render/components/control/scroll.js +++ /dev/null @@ -1,24 +0,0 @@ -let el; - -const initScroll = (elementId) => { - el = document.getElementById(elementId); - el.addEventListener('adjust-scroll', adjustScroll); - window.addEventListener('resize', adjustScroll); -} - -const isBottom = () => { - if (el) { - return el.scrollHeight - el.clientHeight <= el.scrollTop + 1; - } -} - -const adjustScroll = () => { - if (el) { - el.scrollTo({ - top: el.scrollHeight - el.clientHeight, - behavior: 'smooth' - }); - } -} - -export default initScroll; \ No newline at end of file diff --git a/src/render/components/control/text.js b/src/render/components/control/text.js deleted file mode 100644 index 626746f..0000000 --- a/src/render/components/control/text.js +++ /dev/null @@ -1,76 +0,0 @@ -const inputLength = 230; - -const metaKeys = [ - "Tab", - "CapsLock", - "Shift", - "Control", - "Alt", - "Meta", - " ", - "ArrowLeft", - "ArrowRight", - "ArrowUp", - "ArrowDown", - "Enter", - "Backspace", - "Escape" -]; - -function useText(elementId, left, typing) -{ - let el; - - const show = Vue.ref(false); - const leftSide = Vue.ref(false); - - const calcSide = () => { - window.innerWidth - left.value < inputLength ? leftSide.value = true : leftSide.value = false; - } - - /* handle user typing */ - Vue.onMounted(() => { - - el = document.getElementById(elementId); - - window.addEventListener("keydown", (e) => { - - if (!show.value && !metaKeys.includes(e.key)) - { - show.value = true; - } - else if (show.value && ((el.value.length === 1 && e.key === "Backspace") || e.key === "Escape")) - { - show.value = false; - } - - if (e.key === "Enter" && el.value) - { - window.mainApi.send("message", { - category: "text/plain", - text: el.value, - blob: null - }); - show.value = false; - } - }); - - }); - - Vue.watch(left, calcSide); - - Vue.watch(show, (c, _p) => { - if (c) { - el.focus(); - } else { - el.value = ""; - el.blur(); - } - }); - - calcSide(); - - return { leftSide, show }; -} - -export default useText; diff --git a/src/render/components/controllers/bubble.control.ts b/src/render/components/controllers/bubble.control.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/render/components/controllers/helpers.ts b/src/render/components/controllers/helpers.ts new file mode 100644 index 0000000..23976a8 --- /dev/null +++ b/src/render/components/controllers/helpers.ts @@ -0,0 +1,102 @@ +import { ref } from "vue"; +import anime from "animejs"; +import { v4 as uuidv4 } from 'uuid'; + + +export function animateTextInput () { + + const side = ref("right"); + + function show () { + const t1 = (side.value === "right") ? 50 : -70; + const t2 = (side.value === "right") ? 110 : -130; + + anime({ + targets: '#textInput', + opacity: [0, 1], + translateX: [t1, t2], + scale: [0.3, 1], + duration: 500, + easing: 'easeOutExpo', + }) + + } + + function hide () { + const t = (side.value === "right") ? 50 : -80; + + anime({ + targets: '#textInput', + opacity: [1, 0], + translateX: t, + scale: 0.3, + duration: 500, + easing: 'easeOutExpo', + }) + + } + + function switchSide () { + const t = (side.value === "right") ? -130 : 110; + + anime({ + targets: '#textInput', + translateX: t, + duration: 500, + easing: 'easeOutExpo', + }) + + } + + return { + side, + show, + hide, + switchSide + }; + +} + +export function animateAudioInput () { + + function show () { + anime({ + targets: '#recIcon', + opacity: [0, 0.75], + scale: [0.0, 1], + duration: 250, + easing: 'linear', + }) + } + + function hide () { + anime({ + targets: '#recIcon', + opacity: [0.75, 0], + scale: [1, 0], + duration: 250, + easing: 'linear', + }) + } + + return { + show, + hide + }; + +} + + +export function newMessage ({ + text=false, + audio=false, + context=false, + uid=uuidv4() +}) { + return { + text: text, + audio: audio, + context: context, + uid: uid + }; +} diff --git a/src/render/components/controllers/inputItem.control.audio.ts b/src/render/components/controllers/inputItem.control.audio.ts new file mode 100644 index 0000000..facf30c --- /dev/null +++ b/src/render/components/controllers/inputItem.control.audio.ts @@ -0,0 +1,77 @@ +import { onMounted, onUnmounted, ref, Ref } from "vue"; +import { postMessage } from "@/render/ipc"; +import { newMessage, animateAudioInput } from "./helpers"; +import { invokeReturnAudio, postAudioChunk } from "@/render/ipc"; + +export default function useAudioInputController (typing: Ref) { + + const recording = ref(false); + let mediaRecorder: MediaRecorder; + + const { show, hide } = animateAudioInput(); + + // initialize audio + const conf = {audio: true, video: false} + navigator.mediaDevices.getUserMedia(conf).then((stream: MediaStream) => { + + const options = {mimeType: 'audio/webm'}; + mediaRecorder = new MediaRecorder(stream, options); + + // post any mew audio to backend + mediaRecorder.addEventListener('dataavailable', (e: BlobEvent) => { + e.data.arrayBuffer().then((buff: ArrayBuffer) => { + postAudioChunk(buff); + }); + }); + + // get audio and post new message to backend + mediaRecorder.addEventListener('stop', (_e: Event) => { + invokeReturnAudio().then((audio: ArrayBuffer[] | Error) => { + console.log(audio); + // postMessage(newMessage({audio: audio})); + }); + }); + + }); + + // start recording on space bar + const record = () => { + console.log("INPT:Capturing audio...") + mediaRecorder.start(); + recording.value = true; + show() + } + + // stop recording and send on release + const stop = () => { + console.log("INPT:Stopping record.") + mediaRecorder.stop(); + recording.value = false; + hide(); + } + + const onKeyDown = (e: KeyboardEvent) => { + if (e.keyCode == 32 && !typing.value) record(); + } + + const onKeyUp = (e: KeyboardEvent) => { + if (e.keyCode == 32 && recording.value) stop(); + } + + //----------------------------------------------------------- + + onMounted(() => { + window.addEventListener("keydown", onKeyDown); + window.addEventListener("keyup", onKeyUp); + }); + + onUnmounted(() => { + window.removeEventListener("keydown", onKeyDown); + window.removeEventListener("keyup", onKeyUp); + }) + + return { + recording + } + +} diff --git a/src/render/components/controllers/inputItem.control.text.ts b/src/render/components/controllers/inputItem.control.text.ts new file mode 100644 index 0000000..941ae89 --- /dev/null +++ b/src/render/components/controllers/inputItem.control.text.ts @@ -0,0 +1,134 @@ +import { Ref, ref, watch, onMounted, onUnmounted } from "vue"; +import { postMessage } from "@/render/ipc"; +import { newMessage, animateTextInput } from "./helpers"; + + +export default function useTextInputController(elementX: Ref) { + + let textInput: HTMLInputElement | null; + + const { side, show, hide, switchSide } = animateTextInput(); + + let firstKey = true; + const typing = ref(false); + + // Prep inputItem for typing. + const prepInput = () => { + show() + typing.value = true + } + + // Clear and hide inputItem after done typing. + const clearInput = () => { + + if (textInput) { + textInput.value = ""; + textInput.blur(); + } + + hide() + firstKey = true; + typing.value = false; + } + + // Send a message and clean up after. + const sendMessage = () => { + if (textInput) { + + // Send it to the backend for processing. + const message = newMessage({ + text: false + }); + + // postMessage(message); + + clearInput() + } + } + + // Keys that are capable of opening the text input (numbers and letters). + const isHotKey = (key: number) => { + if (key >= 47 && key <= 91) { // a letter + return true + } + } + + //---Callbacks----------------------------------------------- + + const onKeyDown = (e: KeyboardEvent) => { + const key = e.keyCode; + + if (textInput) { + + // Only runs on firstKey. + if (firstKey) { + + if (!isHotKey(key)) { + return + } + + prepInput() + } + + textInput.focus(); + + // Close input when no text or on ESC. + if ((textInput.value == "") && (!firstKey) && (key === 8)) { // backspace + clearInput() + return + } + + if (key === 27) { // escape + clearInput() + return + } + + // Close and send on enter. + if (key === 13) { + if (textInput.value) { + sendMessage() + return + } + } + + if (firstKey) firstKey = false + } + } + + //----------------------------------------------------------- + + // Watch parent position and update side. + watch(elementX, (elementX, _previous) => { + const winW = window.innerWidth + + // Logic depends on the side we are on. + if (side.value === "right") { + if (winW - elementX < 230) { + switchSide() + side.value = "left" + } + } + + else { + if (winW - elementX > 230) { + switchSide() + side.value = "right" + } + } + + }); + + onMounted(() => { + textInput = document.getElementById("textInput") as HTMLInputElement; + window.addEventListener("keydown", onKeyDown); + }) + + onUnmounted(() => { + window.removeEventListener("keydown", onKeyDown); + }); + + return { + typing + } + +} diff --git a/src/render/components/controllers/messenger.control.ts b/src/render/components/controllers/messenger.control.ts new file mode 100644 index 0000000..b9c3a96 --- /dev/null +++ b/src/render/components/controllers/messenger.control.ts @@ -0,0 +1,38 @@ +import { ref } from 'vue'; +import useScroll from "@/render/composables/useScroll"; + +const messagesRef = ref(); + +/* seed the canvas with messages */ +const seedCanvas = (messages: Message[]) => { + messagesRef.value = messages; +} + +const addMessage = (message: Message) => { + messagesRef.value.push(message); +} + +const updateMessage = (message: Message) => { + + let target_message = messagesRef.value.filter((m: Message) => { + return m.uid = message.uid; + })[0]; + + if (target_message) { + target_message = message; + } + +} + +export default function useMessages() { + + const { updateScrollRef, adjustScroll } = useScroll("messenger"); + + return { + messagesRef, + seedCanvas, + addMessage, + updateMessage + }; + +} diff --git a/src/render/components/header.js b/src/render/components/header.js deleted file mode 100644 index 0851a69..0000000 --- a/src/render/components/header.js +++ /dev/null @@ -1,23 +0,0 @@ -const Header = -{ - setup() { - const onNavBar = (command) => window.mainApi.send('nav-bar', command); - return { onNavBar }; - }, - - template: ` - - - ` -} - -/** Shake a div to indicate incorrect credentials */ -const shake = (elementId) => -{ - const el = document.getElementById(elementId); - - if (el) - el.classList.remove("shake"); - - setTimeout(() => { - if (el) el.classList.add("shake"); - }, 250); -} - -export default Login; \ No newline at end of file diff --git a/src/render/components/login.vue b/src/render/components/login.vue new file mode 100644 index 0000000..bece6ea --- /dev/null +++ b/src/render/components/login.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/src/render/components/message.js b/src/render/components/message.js deleted file mode 100644 index fd1b41c..0000000 --- a/src/render/components/message.js +++ /dev/null @@ -1,60 +0,0 @@ -import Bubble from "./bubble.js"; -import Context from "./context.js"; - -const Message = -{ - components: { - Bubble, - Context - }, - - props: ["modifier", "content", "context", "avatar", "id"], - - setup(props) - { - return { calcChild }; - }, - - template: ` -
- - - - - -
` -} - -function calcChild(index, len) -{ - if (len === 1) - { - return "none-child"; - } - else if (index === 0) - { - return "first-child"; - } - else if (index === len - 1) - { - return "last-child"; - } - else - { - return "middle-child"; - } -} - -export default Message; diff --git a/src/render/components/messenger.js b/src/render/components/messenger.js deleted file mode 100644 index 668b505..0000000 --- a/src/render/components/messenger.js +++ /dev/null @@ -1,102 +0,0 @@ -import initScroll from "./control/scroll.js"; - -import WS from "./ws.js"; -import Input from "./input.js"; -import Settings from "./settings.js"; -import Message from "./message.js"; - -const Messenger = { - - components: { - WS, - Settings, - Message, - Input - }, - - setup() - { - const person = Vue.ref(""); - const messages = Vue.ref([{ - modifier: "ai", - content: [{ - category: "text/html", - text: `
-

About HTML Parser

-

Return the text of the most recently opened start tag. This should not normally be needed for structured processing, but may be useful in dealing with HTML “as deployed” or for re-generating input with minimal changes (whitespace between attributes can be preserved, etc.).

-
`, - blob: null - }], - context: "Email from David Spade", - avatar: null, - id: 0 - }]); - - const onDrop = async (e) => { - const file = e.dataTransfer.items[0].getAsFile(); - - const buffer = await file.arrayBuffer(); - const b64String = await window.mainApi.invoke("encode", buffer); - - window.mainApi.send("message", { - category: file.type, - text: file.name, - blob: b64String - }); - } - - Vue.onMounted(() => { - - window.mainApi.on("message", (message) => { - if (message.person) - { - person.value = message.person; - messages.value = message.messages; - } - else if (message.name) - { - person.value = message; - } - else if (message.content) - { - messages.value.push(message); - } - else - { - messages.value[messages.value.length - 1].content.push(message); - } - }); - - initScroll("messenger"); - window.mainApi.send("messenger"); - }); - - Vue.onUnmounted(() => { - messages.value = []; - }); - - return { person, messages, onDrop }; - }, - - template: ` - - - - -
- -
` -} - -export default Messenger; diff --git a/src/render/components/messenger.vue b/src/render/components/messenger.vue new file mode 100644 index 0000000..ee56549 --- /dev/null +++ b/src/render/components/messenger.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/render/components/settings.js b/src/render/components/settings.js deleted file mode 100644 index 68d69f2..0000000 --- a/src/render/components/settings.js +++ /dev/null @@ -1,28 +0,0 @@ - -const Settings = -{ - setup() { - const active = Vue.ref(false); - - window.addEventListener("keydown", (e) => { - console.log("toggle settings"); - }) - - const logout = async () => { - await window.mainApi.send("logout"); - } - - return { active, logout }; - }, - - template: ` -
- - - -
` -} - -export default Settings; \ No newline at end of file diff --git a/src/render/components/settings.vue b/src/render/components/settings.vue new file mode 100644 index 0000000..cf881c3 --- /dev/null +++ b/src/render/components/settings.vue @@ -0,0 +1,164 @@ + + + + + + diff --git a/src/render/components/splash.js b/src/render/components/splash.js deleted file mode 100644 index 220eaad..0000000 --- a/src/render/components/splash.js +++ /dev/null @@ -1,50 +0,0 @@ -const Splash = -{ - template: ` -
- - - - - - - - - - - - - - -
- ` -} - -export default Splash; \ No newline at end of file diff --git a/src/render/components/splash.vue b/src/render/components/splash.vue new file mode 100644 index 0000000..247847a --- /dev/null +++ b/src/render/components/splash.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/render/components/ws.js b/src/render/components/ws.js deleted file mode 100644 index 189b03d..0000000 --- a/src/render/components/ws.js +++ /dev/null @@ -1,35 +0,0 @@ - -const WS = -{ - setup() - { - const status = Vue.ref(); - - Vue.onMounted(() => { - - window.mainApi.on("ws-status", (val) => { - status.value = val; - }); - - }); - - return { status } - }, - - template: ` -
- - - -
-
-
- -
` -} - -export default WS; \ No newline at end of file diff --git a/src/render/composables/useDraggify.ts b/src/render/composables/useDraggify.ts new file mode 100644 index 0000000..f3d8978 --- /dev/null +++ b/src/render/composables/useDraggify.ts @@ -0,0 +1,199 @@ +import { onMounted, ref, onUnmounted } from "vue"; + +//---Dragabble Helper Funcs-------------------------------------- + +// Calculate distance to nearest side. +function calcSideProximity (elementX: number, winW: number) { + + // Calc right short. + let short = winW - elementX - 40; + + // See if it's left short. + if (elementX + 20 < winW / 2) { + short = elementX + } + + return short +} + +// Update elementX or elementY value on window resize +function calcPosition (elementPosition: number, percent: number, + short: number, win: number) { + + // Is it close to the right/bottom side? + if (percent > 0.75) { + elementPosition = win - short - 40; + } + + // Is it not close to a side? + if (percent < 0.75 && percent > 0.25) { + elementPosition = win * percent + } + + return elementPosition +} + +export default function draggify(elementId: string, xStart: number, + yStart: number, margin: number) { + + let element: HTMLElement | null; + + // Cords of inputItem. + const elementX = ref(0); + const elementY = ref(0); + + // Position of inputItem on terms of percentage of window. + let percentX: number; + let percentY: number; + + // How close inputItem is to closest X or Y side. + let xShort: number; + let yShort: number; + + // Keep track of window size; + let winW = window.innerWidth; + let winH = window.innerHeight; + + //---Reposition Anime----------------------------------------------- + + // Move element to target smoothly. + const repositionAnime = (xChange: number, yChange: number) => { + const xStep = xChange / 6000; + const yStep = yChange / 6000; + + for (let i = 1; i <= 6000; i++) { + + setTimeout(() => { + elementX.value += xStep; + elementY.value += yStep; + }, 16) // 60fps + + } + } + + //---Event Handlers----------------------------------------------- + + // Update the position of inputItem on mouse dragging. + const onMouseMove = (e: any) => { + e.preventDefault() + element = document.getElementById(elementId); + + if (element) { + const inputItemRect = element.getBoundingClientRect(); + elementX.value = inputItemRect.left + e.movementX; + elementY.value = inputItemRect.top + e.movementY; + } + + } + + // Add an event listener for dragging. + const onMouseDown = (_e: any) => { + _e.preventDefault() + window.addEventListener('mousemove', onMouseMove, true); + } + + // Update position references when user is done moving targetEl. + const onMouseUp = (_e: any) => { + window.removeEventListener('mousemove', onMouseMove, true); + + // See if and calculate reposition. + let x = 0; // vector change + let y = 0; + + if (elementX.value < 0) { + x = (elementX.value - margin)*-1 + } + + if (elementX.value > winW - 40) { + const b = winW - 40 - margin; + x = (elementX.value - b)*-1 + } + + // Reposition y + if (elementY.value < 0) { + y = (elementY.value - margin)*-1 + } + + if (elementY.value > winH - 40) { + const d = winH - 40 - margin; + y = (elementY.value - d)*-1 + } + + // Reposition if needed. + if (x !== 0 || y !== 0) repositionAnime(x, y); + + xShort = calcSideProximity(elementX.value, winW); + yShort = calcSideProximity(elementY.value, winH); + + // Update percentages. + percentX = elementX.value / winW; + percentY = elementY.value / winH; + + // Save position. + const position = { + x: elementX.value, + y: elementY.value + } + window.localStorage.setItem("inputItem_position", JSON.stringify(position)); + } + + // Update position of targetEl on windowResize. + const onWindowResize = (_e: any) => { + + // Update window dimensions. + winW = window.innerWidth; + winH = window.innerHeight; + + elementX.value = calcPosition(elementX.value, percentX, xShort, winW); + elementY.value = calcPosition(elementY.value, percentY, yShort, winH); + + } + + //--------------------------------------------------------------- + + onMounted(() => { + element = document.getElementById(elementId); + + if (element) { + element.addEventListener('mousedown', onMouseDown, false); + } + + window.addEventListener("mouseup", onMouseUp, false); + + // Initialize the positional references. + percentX = elementX.value / window.innerWidth; + percentY = elementY.value / window.innerHeight; + + xShort = calcSideProximity(elementX.value, window.innerWidth); + yShort = calcSideProximity(elementX.value, window.innerHeight); + + // Then, we can listen for window resize. + window.addEventListener('resize', onWindowResize, false); + }); + + // Try loading initPosition, otherwise set default values + let initPosition: any; + const rawData = window.localStorage.getItem("inputItem_position") + + if (rawData) { + initPosition = JSON.parse(rawData) + } else { + initPosition = {x: xStart, y: yStart} + } + + elementX.value = initPosition.x; + elementY.value = initPosition.y; + + // remove event listeners on component dismount. + onUnmounted(() => { + window.removeEventListener('resize', onWindowResize); + window.removeEventListener('mouseup', onMouseUp); + if (element) element.removeEventListener('mousedown', onMouseDown); + }) + + return { + elementX, + elementY + } + +} diff --git a/src/render/composables/useIpcRend.ts b/src/render/composables/useIpcRend.ts new file mode 100644 index 0000000..9ba7f73 --- /dev/null +++ b/src/render/composables/useIpcRend.ts @@ -0,0 +1,54 @@ + +import { IpcRendererEvent } from "electron"; + +export class IpcRendererListener implements IIpcListener { + + readonly channel: string; + + readonly _listenerCallback: IpcListenerCallback; + + constructor(options: { + channel: string; + listenerCallback: IpcListenerCallback; + }) { + this.channel = options.channel; + this._listenerCallback = options.listenerCallback; + } + + listen() { + this.remove(); + window.ipcRenderer.on(this.channel, this._onPost); + } + + remove() { + window.ipcRenderer.removeAllListeners(this.channel); + } + + private _onPost = (_e: IpcRendererEvent, payload: InputParam): void => { + console.log(`[IPC] Post: ${this.channel}`); + this._listenerCallback(payload); + } + +} + + +export default function useIpcRenderer () { + + const invoke = async (endpoint: string, payload: any) => { + try { + const res = await window.ipcRenderer.invoke(endpoint, payload); + return res; + } catch (e) { + throw e; + } + } + + const post = (endpoint: string, payload: any) => { + window.ipcRenderer.send(endpoint, payload); + }; + + return { + invoke, + post, + } +} diff --git a/src/render/composables/useMessages.ts b/src/render/composables/useMessages.ts new file mode 100644 index 0000000..f731584 --- /dev/null +++ b/src/render/composables/useMessages.ts @@ -0,0 +1,33 @@ +// shared +import { ref, Ref } from "vue"; +import useScroll from "@/render/composables/useScroll"; + +export const messages: Ref> = ref([]); + +export const setMessages: IpcListenerCallback> = (payload) => { + messages.value = payload as Array; +} + +export const addMessage: IpcListenerCallback = (payload) => { + messages.value.push(payload as Message); +} + +export const updateMessage: IpcListenerCallback = (payload) => { + const message = payload as Message; + + let targetMessage = messages.value.filter((m: Message) => { + return m.uid = message.uid; + })[0]; + + if (targetMessage) { + targetMessage = message; + } + +} + +export default { + messages, + setMessages, + addMessage, + updateMessage +}; diff --git a/src/render/composables/useProfile.ts b/src/render/composables/useProfile.ts new file mode 100644 index 0000000..3575de7 --- /dev/null +++ b/src/render/composables/useProfile.ts @@ -0,0 +1,27 @@ +// shared +import { ref } from "vue"; + +export const profile = ref(); + +export const authComplete = ref(false); + +export const setProfile: IpcListenerCallback = (payload) => { + payload ? profile.value = payload : clearProfile(); + showRender(); +}; + +export const clearProfile = () => { + profile.value = null; +}; + +export const showRender = () => { + authComplete.value = true; +}; + +export default { + setProfile, + clearProfile, + profile, + showRender, + authComplete, +}; diff --git a/src/render/composables/useScroll.ts b/src/render/composables/useScroll.ts new file mode 100644 index 0000000..811e7fc --- /dev/null +++ b/src/render/composables/useScroll.ts @@ -0,0 +1,29 @@ + + +export default function useScroll(element: string) { + + let isScrolledToBottom: boolean; + const view = document.getElementById(element) + + // Update isScrolledToBottom + const updateScrollRef = () => { + if (view) isScrolledToBottom = view.scrollHeight - view.clientHeight <= view.scrollTop + 1; + return isScrolledToBottom; + } + + // Adjust scroll after we add content to the messenger. + const adjustScroll = () => { + if (view) { + view.scrollTo({ + top: view.scrollHeight - view.clientHeight, + behavior: 'smooth' + }); + } + } + + return { + updateScrollRef, + adjustScroll, + }; + +} \ No newline at end of file diff --git a/src/render/index.html b/src/render/index.html deleted file mode 100644 index e45bb6c..0000000 --- a/src/render/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - -
- - - - - - - - \ No newline at end of file diff --git a/src/render/index.js b/src/render/index.js deleted file mode 100644 index 9757f5a..0000000 --- a/src/render/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import App from "./components/app.js"; -Vue.createApp(App).mount("#app"); diff --git a/src/render/ipc.ts b/src/render/ipc.ts new file mode 100644 index 0000000..bc68049 --- /dev/null +++ b/src/render/ipc.ts @@ -0,0 +1,80 @@ + +import useIpc from "@/render/composables/useIpcRend"; +import * as rendererListeners from "./listeners"; + +const { post, invoke } = useIpc(); + +/** + * + * Account and auth related endpoints + * + */ + + +export const invokeLogin = async ( + payload: LoginPayload +): Promise => ( + await invoke('invoke-account-login', JSON.stringify(payload)) +); + +export const invokeLogout = async (): Promise => ( + await invoke("invoke-account-logout", null) +); + +/** + * + * Audio endpoints + * + */ + +export const postAudioChunk = (chunk: ArrayBuffer): void => ( + post("post-audio-collect", chunk) +); + +export const invokeReturnAudio = async (): Promise => ( + await invoke("invoke-audio-flush", null) +); + +/** + * + * Crimata Platform (session) endpoints + * + */ + +export const invokeSession = async (cid: string): Promise => ( + await invoke("messenger-init", cid) +); + +export const postMessage = (payload: Message): void => ( + post('post-session-send', payload) +); + +export const postAppMount = (): void => ( + post('post-app-mount', null) +); + + +/** + * + * Ipc Renderer Listeners + * + */ + +let ipcListeners: IPCListeners = {}; + +export const initIpcRendererListeners = () => { + for (const [key, listener] of Object.entries(rendererListeners)) { + if (!(key in ipcListeners)) { + ipcListeners[key] = listener; + listener.listen(); + } + } +}; + +export const removeListeners = () => { + for (const [key, listener] of Object.entries(rendererListeners)) { + listener.remove(); + } + ipcListeners = {}; +}; + diff --git a/src/render/listeners.ts b/src/render/listeners.ts new file mode 100644 index 0000000..876d206 --- /dev/null +++ b/src/render/listeners.ts @@ -0,0 +1,32 @@ + +import { IpcRendererListener } from "./composables/useIpcRend" +import { setProfile } from "./composables/useProfile"; +import { setMessages, addMessage, updateMessage } from "./composables/useMessages"; + +const SET_PROFILE_CHANNEL = "set-profile"; + +const INIT_MESSAGES_CHANNEL = "init-messages"; +const ADD_MESSAGE_CHANNEL = "add-message"; +const UPDATE_MESSAGE_CHANNEL = "update-message"; + +export const setProfileListener = new IpcRendererListener({ + channel: SET_PROFILE_CHANNEL, + listenerCallback: setProfile +}); + +export const initMessagesListener = new IpcRendererListener({ + channel: INIT_MESSAGES_CHANNEL, + listenerCallback: setMessages +}); + + +export const addMessagesListener = new IpcRendererListener({ + channel: ADD_MESSAGE_CHANNEL, + listenerCallback: addMessage +}); + +export const updateMessagesListener = new IpcRendererListener({ + channel: UPDATE_MESSAGE_CHANNEL, + listenerCallback: updateMessage +}); + diff --git a/src/render/main.css b/src/render/main.css deleted file mode 100644 index f25adda..0000000 --- a/src/render/main.css +++ /dev/null @@ -1,571 +0,0 @@ -html, body { - margin: 0; - padding: 0; -} - -/* The first word of the class is the component that it modifys. */ - -#splash { - width: 100vw; - height: 100vh; - display: flex; - align-items: center; - justify-content: center; -} - -#app { - font-family: "SF Pro Text"; - -webkit-font-smoothing: antialiased; - height: 100vh; - width: 100vw; - border-radius: 15px; -} - -#header-titlebar { - position: fixed; - width: 100vw; - height: 54px; - opacity: 0.75; - background-color: #EBEBEB; - -webkit-app-region: drag; - border: none; - outline: none; - z-index: 1; -} - -.contacts { - margin: 0; - padding: 0; - border-radius: inherit; - background-color: #DBDBDB; -} - -.contacts > li { - padding: 12px; - display: flex; -} - -.contacts img { - border-radius: 50%; -} - -.contacts .info { - margin-left: 10px; - margin-right: 10px; -} - -.contacts .name { - -} - -.contacts .email { - font-family: SF Compact Display; - font-weight: bold; - font-size: 12px; - color: #898989; -} - -.mail { - padding: 12px; - margin: 0px; - background-color: #D6F2FF; - border-radius: inherit; -} - -.mail h4 { - margin-top: 0px; -} - -.mail p { - margin-bottom: 0px; -} - -#header-menu { - position: fixed; - margin-left: 20px; - margin-top: 20px; - z-index: 2; -} - -.header-menu-button { - min-width: 14px; - min-height: 14px; - border-radius: 7px; -} - -.header-exit-button { - background-color: #FF6157; -} - -.header-exit-button:active { - background: #c14645; -} - -.header-min-button { - background-color: #FFC12F; - margin-left: 8px; -} - -.header-min-button:active { - background-color: #c08e38; -} - -#login { - width: 100vw; - height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -#login-form { - font-family: "SF Compact Display"; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding-bottom: 20px; -} - -.login-input-box { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - margin-bottom: 10px; -} - -.login-input { - background-color: #B9B9B9; - border: none; - outline: none; - padding: 16px; - text-decoration: none; - margin: 4px 2px; - cursor: pointer; - border-radius: 10px; -} - -.login-input-modifier { - min-width: 181px; - font-size: 12px; - font-weight: bold; - text-align: left; - margin-left: 15px; -} - -.login-submit-button { - font-family: "SF Compact Display"; - position: fixed; - margin-top: 260px; - background-color: #58C4FD; - color: white; - padding: 10px 30px; - border-radius: 20px; - font-size: 14px; - font-weight: bold; -} - -.login-submit-button:active { - background-color: #4296C3; -} - -/* generic shaking functionality */ -.shake { - animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both; - transform: translate3d(0, 0, 0); - backface-visibility: hidden; - perspective: 1000px; -} - -@keyframes shake { - 10%, 90% { - transform: translate3d(-1px, 0, 0); - } - - 20%, 80% { - transform: translate3d(2px, 0, 0); - } - - 30%, 50%, 70% { - transform: translate3d(-4px, 0, 0); - } - - 40%, 60% { - transform: translate3d(4px, 0, 0); - } -} - -#messenger { - width: 100vw; - height: 100vh; - overflow: auto; -} - -/* hide native scrollbar */ -#messenger::-webkit-scrollbar { - display: none; -} - -#ws { - position: fixed; - width: 100vw; - height: 54px; - display: flex; - align-items: center; - justify-content: center; - z-index: 1; -} - -#ws-logo { - transition: all 0.5s; -} - -#ws-logo.move { - transform: translateX(15px); -} - -#ws-dots { - opacity: 0; - transition: all 0.5s; -} - -#ws-dots.move { - opacity: 1; - transform: translateX(-15px); -} - -.ws-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background-color: #48E065; - position: relative; - transform: translateX(-15px); - animation: ws-dot-flashing 1s infinite linear alternate; - animation-delay: .25s; -} - -.ws-dot::before, .ws-dot::after { - content: ''; - display: inline-block; - position: absolute; -} - -.ws-dot::before { - width: 6px; - height: 6px; - border-radius: 50%; - background-color: #48E065; - left: -9px; - animation: ws-dot-flashing 1s infinite alternate; - animation-delay: 0s; -} - -.ws-dot::after { - width: 6px; - height: 6px; - border-radius: 50%; - background-color: #48E065; - left: 9px; - animation: ws-dot-flashing 1s infinite alternate; - animation-delay: 0.5s; -} - -@keyframes ws-dot-flashing { - 0% { - background-color: #48E065; - } - 50%, - 100% { - background-color: #9B9B9B; - } -} - -#input { - position: absolute; - display: flex; - align-items: center; - justify-content: center; - padding: 0; - width: 40px; - height: 40px; - z-index: 3; - /* To prevent window drag when overlapping with titlebar. */ - -webkit-app-region: no-drag; - border-radius: 50%; - /* Set opacity here to not affect child. */ - background-color: rgba(235, 235, 235, 0.75); - cursor: pointer; - box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.15); -} - -#input-text { - position: absolute; - min-width: 150px; - height: 16px; - border-radius: 18px; - padding: 10px; - outline: none; - border: none; - pointer-events: none; - background-color: white; - z-index: -1; - box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.15); - transform-origin: center; - opacity: 0; - transform: translateX(70%); - transition: all 0.5s; -} - -#input-text.show { - opacity: 1; -} - -#input-text.leftSide { - transform: translateX(-70%); -} - -#settings { - position: fixed; - width: 100vw; - height: 100vh; - background-color: rgba(235, 235, 235, 0.75); - z-index: 4; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - animation-name: settings-appear; - animation-duration: 0.5s; -} - -@keyframes settings-appear { - from { - background-color: rgba(235, 235, 235, 0); - } - to { - background-color: rgba(235, 235, 235, 0.75); - } -} - -.settings-icon { - position: fixed; - right: 0; - border: none; - outline: none; - display: flex; - flex-direction: row; - padding: 5px; - margin-right: 20px; - margin-top: 19px; - z-index: 2; - background-color: Transparent; -} - -.settings-icon:hover { - cursor: pointer; -} - -.settings-account { - font-size: 12px; - font-weight: bold; - margin-bottom: 25px; -} - -.settings-logout-button { - font-family: "SF Compact Display"; - background-color: #B7B7B7; - padding: 10px 30px; - border-radius: 20px; - font-size: 14px; - font-weight: bold; -} - -.settings-logout-button:hover { - cursor: pointer; -} - -.settings-version { - position: absolute; - left: 50%; - top: 75%; - transform: translate(-50%, -50%); - font-size: 12px; - font-weight: bold; - color: #575757; -} - -.message { - width: 100vw; - display: flex; - flex-direction: column; - padding-top: 9px; - padding-bottom: 9px; - animation-name: message-init-anim; - animation-duration: 0.25s; -} - -@keyframes message-init-anim { - from { - opacity: 0; - } to { - opacity: 1; - } -} - -.message:first-child { - margin-top: 55px; -} - -.message:last-child { - margin-bottom: 6px; -} - -.client-message { - align-items: flex-end; -} - -.ai-message { - align-items: flex-start; -} - -.bubble { - position: relative; - max-width: 66vw; - font-family: "SF Pro Text"; - font-size: 14px; - border-radius: 18px; - margin-bottom: 4px; -} - -.ai-bubble { - background-color: #FFFFFF; - margin-left: 15px; -} - -.client-bubble { - color: white; - background-color: #58C4FD; - margin-right: 15px; -} - -.ai-first-child { - border-bottom-left-radius: 9px; -} - -.ai-middle-child { - border-top-left-radius: 9px; - border-bottom-left-radius: 9px; -} - -.ai-last-child { - border-top-left-radius: 9px; -} - -.client-first-child { - border-bottom-right-radius: 9px; -} - -.client-middle-child { - border-top-right-radius: 9px; - border-bottom-right-radius: 9px; -} - -.client-last-child { - border-top-right-radius: 9px; -} - -.bubble > p { - margin: 0px; - padding: 10px; -} - -.bubble > div { - border-radius: inherit; -} - -.bubble > img { - border-radius: inherit; - display: block; - max-width: inherit; -} - -.bubble a { - border-radius: inherit; - font-family: "SF Compact Rounded"; - background-color: #D9D9D9; - font-weight: bold; - padding: 12px; - color: #727272; - margin: 0px; -} - -.context { - position: relative; - min-height: 14px; - display: flex; - align-items: center; - font-family: "SF Compact Display"; - font-size: 12px; - font-weight: bold; - margin-top: 5px; -} - -.ai-context { - margin-left: 15px; -} - -.client-context { - margin-right: 15px; -} - -.context-avatar { - display: flex; - justify-content: center; - align-items: center; - width: 30px; - height: 30px; - background-color: white; - border-radius: 15px; - margin-right: 5px; -} - -.context-avatar img { - border-radius: 50%; -} - -/* ---------- shared between components ---------- */ - -.button { - border: none; - outline: none; - text-decoration: none; -} - -/*.button:hover { - cursor: pointer; -}*/ - -.audio { - animation-name: audio-anim; - animation-duration: 2s; - animation-iteration-count: infinite; -} - -@keyframes audio-anim { - 0%, - 100% { - box-shadow: 0 0 0 0.4em rgba(195, 195, 195, 0.4), 0 0 0 0.25em rgba(195, 195, 195, 0.15); - } - 25% { - box-shadow: 0 0 0 0.15em rgba(195, 195, 195, 0.15), 0 0 0 0.4em rgba(195, 195, 195, 0.3); - } - 50% { - box-shadow: 0 0 0 0.55em rgba(195, 195, 195, 0.55), 0 0 0 0.15em rgba(195, 195, 195, 0.05); - } - 75% { - box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.25), 0 0 0 0.55em rgba(195, 195, 195, 0.45); - } -} diff --git a/src/render/main.ts b/src/render/main.ts new file mode 100644 index 0000000..5375889 --- /dev/null +++ b/src/render/main.ts @@ -0,0 +1,21 @@ + +// src/main.ts + +import App from "./App.vue"; + +import mitt from "mitt"; +import { createApp } from "vue"; + +import { initIpcRendererListeners } from "./ipc" + + +// Handle ipcMain events. +initIpcRendererListeners(); + +// Handle events. +const emitter = mitt(); + +const app = createApp(App); + +app.provide("mitt", emitter); +app.mount("#app"); diff --git a/src/render/preload.js b/src/render/preload.js deleted file mode 100644 index 9b50adf..0000000 --- a/src/render/preload.js +++ /dev/null @@ -1,53 +0,0 @@ -const { contextBridge, ipcRenderer } = require("electron"); - -/** The main <-> render interface */ - -const validFromMainChannels = [ - "set-account", - "message", - "ws-status", - "record", - "playback" -]; - -const validToMain = [ - "messenger", - "message", - "login", - "logout", - "encode", - "decode" -] - -ipcRenderer.setMaxListeners(250); - -// Expose protected methods that allow the renderer process to use -// the ipcRenderer without exposing the entire object -// TODO: Implement argument filtering for added security -contextBridge.exposeInMainWorld( - "mainApi", { - invoke: async (channel, ...args) => { - if (validToMain.includes(channel)) { - try { - const res = await ipcRenderer.invoke(channel, ...args); - return res; - } catch (e) { - console.log("IPCRenderer.invoke error"); - } - } - }, - send: (channel, ...args) => { - if (validToMain.includes(channel)) { - ipcRenderer.send(channel, ...args); - } - }, - on: (channel, func) => { - if (validFromMainChannels.includes(channel)) { - ipcRenderer.on(channel, (event, ...args) => func(...args)); - } - }, - removeAllListeners: (channel) => { - ipcRenderer.removeAllListeners(channel); - } - } -); diff --git a/src/render/preload.ts b/src/render/preload.ts new file mode 100644 index 0000000..054f672 --- /dev/null +++ b/src/render/preload.ts @@ -0,0 +1,38 @@ +// All of the Node.js APIs are available in the preload process. +// It has the same sandbox as a Chrome extension. +import { ipcRenderer } from "electron"; + +declare global { + interface Window { + ipcRenderer: typeof ipcRenderer; + } +} + +window.ipcRenderer = ipcRenderer; + +process.once("loaded", () => { + + window.addEventListener("message", event => { + + // do something with custom event + const message = event.data; + + if (message.endpoint === "update-menu-bar") { + ipcRenderer.send("update-menu-bar", message.content); + } + + if (message.endpoint === "update-recorder") { + ipcRenderer.send("update-recorder", message.content); + } + + if (message.endpoint === "client-message") { + ipcRenderer.send("client-message", message.content); + } + + if (message.endpoint === "logout") { + ipcRenderer.send("logout", message.content); + } + + }); + +}); diff --git a/src/render/shims-vue.d.ts b/src/render/shims-vue.d.ts new file mode 100644 index 0000000..35a61a9 --- /dev/null +++ b/src/render/shims-vue.d.ts @@ -0,0 +1,13 @@ +declare module "*.vue" { + import { defineComponent } from "vue"; + const component: ReturnType; + export default component; +} + +declare module "anime-js" { + namespace anime { + import * as anime from "animejs/lib/anime.es.js"; + function anime(): void; + export = anime; + } +} diff --git a/src/render/vendor/vue.js b/src/render/vendor/vue.js deleted file mode 100644 index 6e5ee00..0000000 --- a/src/render/vendor/vue.js +++ /dev/null @@ -1,15872 +0,0 @@ -var Vue = (function (exports) { - 'use strict'; - - /** - * Make a map and return a function for checking if a key - * is in that map. - * IMPORTANT: all calls of this function must be prefixed with - * \/\*#\_\_PURE\_\_\*\/ - * So that rollup can tree-shake them if necessary. - */ - function makeMap(str, expectsLowerCase) { - const map = Object.create(null); - const list = str.split(','); - for (let i = 0; i < list.length; i++) { - map[list[i]] = true; - } - return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val]; - } - - /** - * dev only flag -> name mapping - */ - const PatchFlagNames = { - [1 /* TEXT */]: `TEXT`, - [2 /* CLASS */]: `CLASS`, - [4 /* STYLE */]: `STYLE`, - [8 /* PROPS */]: `PROPS`, - [16 /* FULL_PROPS */]: `FULL_PROPS`, - [32 /* HYDRATE_EVENTS */]: `HYDRATE_EVENTS`, - [64 /* STABLE_FRAGMENT */]: `STABLE_FRAGMENT`, - [128 /* KEYED_FRAGMENT */]: `KEYED_FRAGMENT`, - [256 /* UNKEYED_FRAGMENT */]: `UNKEYED_FRAGMENT`, - [512 /* NEED_PATCH */]: `NEED_PATCH`, - [1024 /* DYNAMIC_SLOTS */]: `DYNAMIC_SLOTS`, - [2048 /* DEV_ROOT_FRAGMENT */]: `DEV_ROOT_FRAGMENT`, - [-1 /* HOISTED */]: `HOISTED`, - [-2 /* BAIL */]: `BAIL` - }; - - /** - * Dev only - */ - const slotFlagsText = { - [1 /* STABLE */]: 'STABLE', - [2 /* DYNAMIC */]: 'DYNAMIC', - [3 /* FORWARDED */]: 'FORWARDED' - }; - - const GLOBALS_WHITE_LISTED = 'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' + - 'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' + - 'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt'; - const isGloballyWhitelisted = /*#__PURE__*/ makeMap(GLOBALS_WHITE_LISTED); - - const range = 2; - function generateCodeFrame(source, start = 0, end = source.length) { - // Split the content into individual lines but capture the newline sequence - // that separated each line. This is important because the actual sequence is - // needed to properly take into account the full line length for offset - // comparison - let lines = source.split(/(\r?\n)/); - // Separate the lines and newline sequences into separate arrays for easier referencing - const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); - lines = lines.filter((_, idx) => idx % 2 === 0); - let count = 0; - const res = []; - for (let i = 0; i < lines.length; i++) { - count += - lines[i].length + - ((newlineSequences[i] && newlineSequences[i].length) || 0); - if (count >= start) { - for (let j = i - range; j <= i + range || end > count; j++) { - if (j < 0 || j >= lines.length) - continue; - const line = j + 1; - res.push(`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`); - const lineLength = lines[j].length; - const newLineSeqLength = (newlineSequences[j] && newlineSequences[j].length) || 0; - if (j === i) { - // push underline - const pad = start - (count - (lineLength + newLineSeqLength)); - const length = Math.max(1, end > count ? lineLength - pad : end - start); - res.push(` | ` + ' '.repeat(pad) + '^'.repeat(length)); - } - else if (j > i) { - if (end > count) { - const length = Math.max(Math.min(end - count, lineLength), 1); - res.push(` | ` + '^'.repeat(length)); - } - count += lineLength + newLineSeqLength; - } - } - break; - } - } - return res.join('\n'); - } - - /** - * On the client we only need to offer special cases for boolean attributes that - * have different names from their corresponding dom properties: - * - itemscope -> N/A - * - allowfullscreen -> allowFullscreen - * - formnovalidate -> formNoValidate - * - ismap -> isMap - * - nomodule -> noModule - * - novalidate -> noValidate - * - readonly -> readOnly - */ - const specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; - const isSpecialBooleanAttr = /*#__PURE__*/ makeMap(specialBooleanAttrs); - /** - * Boolean attributes should be included if the value is truthy or ''. - * e.g. - const forcePatchValue = (type === 'input' && dirs) || type === 'option'; - // skip props & children if this is hoisted static nodes - if (forcePatchValue || patchFlag !== -1 /* HOISTED */) { - if (dirs) { - invokeDirectiveHook(vnode, null, parentComponent, 'created'); - } - // props - if (props) { - if (forcePatchValue || - !optimized || - patchFlag & (16 /* FULL_PROPS */ | 32 /* HYDRATE_EVENTS */)) { - for (const key in props) { - if ((forcePatchValue && key.endsWith('value')) || - (isOn(key) && !isReservedProp(key))) { - patchProp(el, key, null, props[key]); - } - } - } - else if (props.onClick) { - // Fast path for click listeners (which is most often) to avoid - // iterating through props. - patchProp(el, 'onClick', null, props.onClick); - } - } - // vnode / directive hooks - let vnodeHooks; - if ((vnodeHooks = props && props.onVnodeBeforeMount)) { - invokeVNodeHook(vnodeHooks, parentComponent, vnode); - } - if (dirs) { - invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount'); - } - if ((vnodeHooks = props && props.onVnodeMounted) || dirs) { - queueEffectWithSuspense(() => { - vnodeHooks && invokeVNodeHook(vnodeHooks, parentComponent, vnode); - dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted'); - }, parentSuspense); - } - // children - if (shapeFlag & 16 /* ARRAY_CHILDREN */ && - // skip if element has innerHTML / textContent - !(props && (props.innerHTML || props.textContent))) { - let next = hydrateChildren(el.firstChild, vnode, el, parentComponent, parentSuspense, slotScopeIds, optimized); - let hasWarned = false; - while (next) { - hasMismatch = true; - if (!hasWarned) { - warn$1(`Hydration children mismatch in <${vnode.type}>: ` + - `server rendered element contains more child nodes than client vdom.`); - hasWarned = true; - } - // The SSRed DOM contains more nodes than it should. Remove them. - const cur = next; - next = next.nextSibling; - remove(cur); - } - } - else if (shapeFlag & 8 /* TEXT_CHILDREN */) { - if (el.textContent !== vnode.children) { - hasMismatch = true; - warn$1(`Hydration text content mismatch in <${vnode.type}>:\n` + - `- Client: ${el.textContent}\n` + - `- Server: ${vnode.children}`); - el.textContent = vnode.children; - } - } - } - return el.nextSibling; - }; - const hydrateChildren = (node, parentVNode, container, parentComponent, parentSuspense, slotScopeIds, optimized) => { - optimized = optimized || !!parentVNode.dynamicChildren; - const children = parentVNode.children; - const l = children.length; - let hasWarned = false; - for (let i = 0; i < l; i++) { - const vnode = optimized - ? children[i] - : (children[i] = normalizeVNode(children[i])); - if (node) { - node = hydrateNode(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized); - } - else if (vnode.type === Text && !vnode.children) { - continue; - } - else { - hasMismatch = true; - if (!hasWarned) { - warn$1(`Hydration children mismatch in <${container.tagName.toLowerCase()}>: ` + - `server rendered element contains fewer child nodes than client vdom.`); - hasWarned = true; - } - // the SSRed DOM didn't contain enough nodes. Mount the missing ones. - patch(null, vnode, container, null, parentComponent, parentSuspense, isSVGContainer(container), slotScopeIds); - } - } - return node; - }; - const hydrateFragment = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => { - const { slotScopeIds: fragmentSlotScopeIds } = vnode; - if (fragmentSlotScopeIds) { - slotScopeIds = slotScopeIds - ? slotScopeIds.concat(fragmentSlotScopeIds) - : fragmentSlotScopeIds; - } - const container = parentNode(node); - const next = hydrateChildren(nextSibling(node), vnode, container, parentComponent, parentSuspense, slotScopeIds, optimized); - if (next && isComment(next) && next.data === ']') { - return nextSibling((vnode.anchor = next)); - } - else { - // fragment didn't hydrate successfully, since we didn't get a end anchor - // back. This should have led to node/children mismatch warnings. - hasMismatch = true; - // since the anchor is missing, we need to create one and insert it - insert((vnode.anchor = createComment(`]`)), container, next); - return next; - } - }; - const handleMismatch = (node, vnode, parentComponent, parentSuspense, slotScopeIds, isFragment) => { - hasMismatch = true; - warn$1(`Hydration node mismatch:\n- Client vnode:`, vnode.type, `\n- Server rendered DOM:`, node, node.nodeType === 3 /* TEXT */ - ? `(text)` - : isComment(node) && node.data === '[' - ? `(start of fragment)` - : ``); - vnode.el = null; - if (isFragment) { - // remove excessive fragment nodes - const end = locateClosingAsyncAnchor(node); - while (true) { - const next = nextSibling(node); - if (next && next !== end) { - remove(next); - } - else { - break; - } - } - } - const next = nextSibling(node); - const container = parentNode(node); - remove(node); - patch(null, vnode, container, next, parentComponent, parentSuspense, isSVGContainer(container), slotScopeIds); - return next; - }; - const locateClosingAsyncAnchor = (node) => { - let match = 0; - while (node) { - node = nextSibling(node); - if (node && isComment(node)) { - if (node.data === '[') - match++; - if (node.data === ']') { - if (match === 0) { - return nextSibling(node); - } - else { - match--; - } - } - } - } - return node; - }; - return [hydrate, hydrateNode]; - } - - let supported; - let perf; - function startMeasure(instance, type) { - if (instance.appContext.config.performance && isSupported()) { - perf.mark(`vue-${type}-${instance.uid}`); - } - { - devtoolsPerfStart(instance, type, supported ? perf.now() : Date.now()); - } - } - function endMeasure(instance, type) { - if (instance.appContext.config.performance && isSupported()) { - const startTag = `vue-${type}-${instance.uid}`; - const endTag = startTag + `:end`; - perf.mark(endTag); - perf.measure(`<${formatComponentName(instance, instance.type)}> ${type}`, startTag, endTag); - perf.clearMarks(startTag); - perf.clearMarks(endTag); - } - { - devtoolsPerfEnd(instance, type, supported ? perf.now() : Date.now()); - } - } - function isSupported() { - if (supported !== undefined) { - return supported; - } - /* eslint-disable no-restricted-globals */ - if (typeof window !== 'undefined' && window.performance) { - supported = true; - perf = window.performance; - } - else { - supported = false; - } - /* eslint-enable no-restricted-globals */ - return supported; - } - - const queuePostRenderEffect = queueEffectWithSuspense - ; - /** - * The createRenderer function accepts two generic arguments: - * HostNode and HostElement, corresponding to Node and Element types in the - * host environment. For example, for runtime-dom, HostNode would be the DOM - * `Node` interface and HostElement would be the DOM `Element` interface. - * - * Custom renderers can pass in the platform specific types like this: - * - * ``` js - * const { render, createApp } = createRenderer({ - * patchProp, - * ...nodeOps - * }) - * ``` - */ - function createRenderer(options) { - return baseCreateRenderer(options); - } - // Separate API for creating hydration-enabled renderer. - // Hydration logic is only used when calling this function, making it - // tree-shakable. - function createHydrationRenderer(options) { - return baseCreateRenderer(options, createHydrationFunctions); - } - // implementation - function baseCreateRenderer(options, createHydrationFns) { - { - const target = getGlobalThis(); - target.__VUE__ = true; - setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__); - } - const { insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, setScopeId: hostSetScopeId = NOOP, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options; - // Note: functions inside this closure should use `const xxx = () => {}` - // style in order to prevent being inlined by minifiers. - const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, slotScopeIds = null, optimized = isHmrUpdating ? false : !!n2.dynamicChildren) => { - if (n1 === n2) { - return; - } - // patching & not same type, unmount old tree - if (n1 && !isSameVNodeType(n1, n2)) { - anchor = getNextHostNode(n1); - unmount(n1, parentComponent, parentSuspense, true); - n1 = null; - } - if (n2.patchFlag === -2 /* BAIL */) { - optimized = false; - n2.dynamicChildren = null; - } - const { type, ref, shapeFlag } = n2; - switch (type) { - case Text: - processText(n1, n2, container, anchor); - break; - case Comment: - processCommentNode(n1, n2, container, anchor); - break; - case Static: - if (n1 == null) { - mountStaticNode(n2, container, anchor, isSVG); - } - else { - patchStaticNode(n1, n2, container, isSVG); - } - break; - case Fragment: - processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized); - break; - default: - if (shapeFlag & 1 /* ELEMENT */) { - processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized); - } - else if (shapeFlag & 6 /* COMPONENT */) { - processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized); - } - else if (shapeFlag & 64 /* TELEPORT */) { - type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, internals); - } - else if (shapeFlag & 128 /* SUSPENSE */) { - type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, internals); - } - else { - warn$1('Invalid VNode type:', type, `(${typeof type})`); - } - } - // set ref - if (ref != null && parentComponent) { - setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2); - } - }; - const processText = (n1, n2, container, anchor) => { - if (n1 == null) { - hostInsert((n2.el = hostCreateText(n2.children)), container, anchor); - } - else { - const el = (n2.el = n1.el); - if (n2.children !== n1.children) { - hostSetText(el, n2.children); - } - } - }; - const processCommentNode = (n1, n2, container, anchor) => { - if (n1 == null) { - hostInsert((n2.el = hostCreateComment(n2.children || '')), container, anchor); - } - else { - // there's no support for dynamic comments - n2.el = n1.el; - } - }; - const mountStaticNode = (n2, container, anchor, isSVG) => { - [n2.el, n2.anchor] = hostInsertStaticContent(n2.children, container, anchor, isSVG); - }; - /** - * Dev / HMR only - */ - const patchStaticNode = (n1, n2, container, isSVG) => { - // static nodes are only patched during dev for HMR - if (n2.children !== n1.children) { - const anchor = hostNextSibling(n1.anchor); - // remove existing - removeStaticNode(n1); - [n2.el, n2.anchor] = hostInsertStaticContent(n2.children, container, anchor, isSVG); - } - else { - n2.el = n1.el; - n2.anchor = n1.anchor; - } - }; - const moveStaticNode = ({ el, anchor }, container, nextSibling) => { - let next; - while (el && el !== anchor) { - next = hostNextSibling(el); - hostInsert(el, container, nextSibling); - el = next; - } - hostInsert(anchor, container, nextSibling); - }; - const removeStaticNode = ({ el, anchor }) => { - let next; - while (el && el !== anchor) { - next = hostNextSibling(el); - hostRemove(el); - el = next; - } - hostRemove(anchor); - }; - const processElement = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => { - isSVG = isSVG || n2.type === 'svg'; - if (n1 == null) { - mountElement(n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized); - } - else { - patchElement(n1, n2, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized); - } - }; - const mountElement = (vnode, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => { - let el; - let vnodeHook; - const { type, props, shapeFlag, transition, patchFlag, dirs } = vnode; - { - el = vnode.el = hostCreateElement(vnode.type, isSVG, props && props.is, props); - // mount children first, since some props may rely on child content - // being already rendered, e.g. `