diff --git a/.eslintrc.js b/.eslintrc.js index 59f3401f..918865f4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,7 +26,7 @@ module.exports = { 'import/newline-after-import': 'error', 'import/no-default-export': 'off', 'import/no-duplicates': 'error', - 'import/no-unresolved': ['error', { ignore: ['^virtual:', '\\?inline$', '\\?raw$', '\\?asset&asarUnpack', '^youtubei.js$'] }], + 'import/no-unresolved': ['error', { ignore: ['^virtual:', '\\?inline$', '\\?raw$', '\\?asset&asarUnpack'] }], 'import/order': [ 'error', { @@ -67,4 +67,14 @@ module.exports = { es6: true, }, ignorePatterns: ['dist', 'node_modules'], + root: true, + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts'] + }, + 'import/resolver': { + typescript: {}, + exports: {}, + }, + }, }; diff --git a/.gitignore b/.gitignore index ec2a06da..6cb8c059 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ electron-builder.yml !.yarn/releases !.yarn/sdks !.yarn/versions +.vite-inspect diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 5d8dafae..3d558c99 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -1,19 +1,27 @@ +import { resolve } from 'node:path'; + import { defineConfig, defineViteConfig } from 'electron-vite'; import builtinModules from 'builtin-modules'; import viteResolve from 'vite-plugin-resolve'; +import Inspect from 'vite-plugin-inspect'; -import { pluginVirtualModuleGenerator } from './vite-plugins/plugin-virtual-module-generator'; +import { pluginVirtualModuleGenerator } from './vite-plugins/plugin-importer'; +import pluginLoader from './vite-plugins/plugin-loader'; import type { UserConfig } from 'vite'; +const resolveAlias = { + '@': resolve(__dirname, './src'), + '@assets': resolve(__dirname, './assets'), +}; + export default defineConfig({ main: defineViteConfig(({ mode }) => { const commonConfig: UserConfig = { plugins: [ + pluginLoader('backend'), viteResolve({ - 'virtual:PluginBuilders': pluginVirtualModuleGenerator('index'), - 'virtual:MainPlugins': pluginVirtualModuleGenerator('main'), - 'virtual:MenuPlugins': pluginVirtualModuleGenerator('menu'), + 'virtual:plugins': pluginVirtualModuleGenerator('main'), }), ], publicDir: 'assets', @@ -31,9 +39,15 @@ export default defineConfig({ input: './src/index.ts', }, }, + resolve: { + alias: resolveAlias, + }, }; if (mode === 'development') { + commonConfig.plugins?.push( + Inspect({ build: true, outputDir: '.vite-inspect/backend' }), + ); return commonConfig; } @@ -49,9 +63,9 @@ export default defineConfig({ preload: defineViteConfig(({ mode }) => { const commonConfig: UserConfig = { plugins: [ + pluginLoader('preload'), viteResolve({ - 'virtual:PluginBuilders': pluginVirtualModuleGenerator('index'), - 'virtual:PreloadPlugins': pluginVirtualModuleGenerator('preload'), + 'virtual:plugins': pluginVirtualModuleGenerator('preload'), }), ], build: { @@ -66,11 +80,17 @@ export default defineConfig({ rollupOptions: { external: ['electron', 'custom-electron-prompt', ...builtinModules], input: './src/preload.ts', - } + }, + }, + resolve: { + alias: resolveAlias, }, }; if (mode === 'development') { + commonConfig.plugins?.push( + Inspect({ build: true, outputDir: '.vite-inspect/preload' }), + ); return commonConfig; } @@ -86,9 +106,9 @@ export default defineConfig({ renderer: defineViteConfig(({ mode }) => { const commonConfig: UserConfig = { plugins: [ + pluginLoader('renderer'), viteResolve({ - 'virtual:PluginBuilders': pluginVirtualModuleGenerator('index'), - 'virtual:RendererPlugins': pluginVirtualModuleGenerator('renderer'), + 'virtual:plugins': pluginVirtualModuleGenerator('renderer'), }), ], root: './src/', @@ -107,9 +127,15 @@ export default defineConfig({ input: './src/index.html', }, }, + resolve: { + alias: resolveAlias, + }, }; if (mode === 'development') { + commonConfig.plugins?.push( + Inspect({ build: true, outputDir: '.vite-inspect/renderer' }), + ); return commonConfig; } diff --git a/package.json b/package.json index 30c27bbe..77f26742 100644 --- a/package.json +++ b/package.json @@ -94,11 +94,12 @@ "test": "playwright test", "test:debug": "cross-env DEBUG=pw:*,-pw:test:protocol playwright test", "build": "electron-vite build", + "vite:inspect": "yarpm-pnpm run clean && electron-vite build --mode development && yarpm-pnpm exec serve .vite-inspect", "start": "electron-vite preview", "start:debug": "cross-env ELECTRON_ENABLE_LOGGING=1 yarpm-pnpm run start", "dev": "electron-vite dev --watch", "dev:debug": "cross-env ELECTRON_ENABLE_LOGGING=1 yarpm-pnpm run dev", - "clean": "del-cli dist && del-cli pack", + "clean": "del-cli dist && del-cli pack && del-cli .vite-inspect", "dist": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --win --mac --linux -p never", "dist:linux": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --linux -p never", "dist:mac": "yarpm-pnpm run clean && yarpm-pnpm run build && electron-builder --mac dmg:x64 -p never", @@ -136,6 +137,8 @@ "dependencies": { "@cliqz/adblocker-electron": "1.26.11", "@cliqz/adblocker-electron-preload": "1.26.11", + "@electron-toolkit/tsconfig": "^1.0.1", + "@electron/remote": "2.1.0", "@ffmpeg.wasm/core-mt": "0.12.0", "@ffmpeg.wasm/main": "0.12.0", "@foobar404/wave": "2.0.4", @@ -164,7 +167,9 @@ "keyboardevents-areequal": "0.2.2", "node-html-parser": "6.1.11", "node-id3": "0.2.6", + "serve": "^14.2.1", "simple-youtube-age-restriction-bypass": "git+https://github.com/organization/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.8", + "ts-morph": "^20.0.0", "vudio": "2.1.1", "x11": "2.3.0", "youtubei.js": "7.0.0" @@ -175,7 +180,7 @@ "@types/electron-localshortcut": "3.1.3", "@types/howler": "2.2.11", "@types/html-to-text": "9.0.4", - "@typescript-eslint/eslint-plugin": "6.10.0", + "@typescript-eslint/eslint-plugin": "6.12.0", "bufferutil": "4.0.8", "builtin-modules": "^3.3.0", "cross-env": "7.0.3", @@ -184,7 +189,9 @@ "electron-builder": "24.6.4", "electron-devtools-installer": "3.2.0", "electron-vite": "1.0.28", - "eslint": "8.53.0", + "eslint": "8.54.0", + "eslint-import-resolver-exports": "1.0.0-beta.5", + "eslint-import-resolver-typescript": "3.6.1", "eslint-plugin-import": "2.29.0", "eslint-plugin-prettier": "5.0.1", "glob": "10.3.10", @@ -194,6 +201,7 @@ "typescript": "5.2.2", "utf-8-validate": "6.0.3", "vite": "4.5.0", + "vite-plugin-inspect": "^0.7.42", "vite-plugin-resolve": "2.5.1", "ws": "8.14.2", "yarpm": "1.2.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6fe005dc..c7443f23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,12 @@ dependencies: '@cliqz/adblocker-electron-preload': specifier: 1.26.11 version: 1.26.11(electron@27.0.4) + '@electron-toolkit/tsconfig': + specifier: ^1.0.1 + version: 1.0.1(@types/node@20.8.6) + '@electron/remote': + specifier: 2.1.0 + version: 2.1.0(electron@27.0.4) '@ffmpeg.wasm/core-mt': specifier: 0.12.0 version: 0.12.0 @@ -103,9 +109,15 @@ dependencies: node-id3: specifier: 0.2.6 version: 0.2.6 + serve: + specifier: ^14.2.1 + version: 14.2.1 simple-youtube-age-restriction-bypass: specifier: git+https://github.com/organization/Simple-YouTube-Age-Restriction-Bypass.git#v2.5.8 version: github.com/organization/Simple-YouTube-Age-Restriction-Bypass/816a882c68fcfe6cdd9410a6877b88093ed15b28 + ts-morph: + specifier: ^20.0.0 + version: 20.0.0 vudio: specifier: 2.1.1 version: 2.1.1 @@ -133,8 +145,8 @@ devDependencies: specifier: 9.0.4 version: 9.0.4 '@typescript-eslint/eslint-plugin': - specifier: 6.10.0 - version: 6.10.0(@typescript-eslint/parser@6.7.5)(eslint@8.53.0)(typescript@5.2.2) + specifier: 6.12.0 + version: 6.12.0(@typescript-eslint/parser@6.7.5)(eslint@8.54.0)(typescript@5.2.2) bufferutil: specifier: 4.0.8 version: 4.0.8 @@ -160,14 +172,20 @@ devDependencies: specifier: 1.0.28 version: 1.0.28(vite@4.5.0) eslint: - specifier: 8.53.0 - version: 8.53.0 + specifier: 8.54.0 + version: 8.54.0 + eslint-import-resolver-exports: + specifier: 1.0.0-beta.5 + version: 1.0.0-beta.5(eslint-plugin-import@2.29.0)(eslint@8.54.0) + eslint-import-resolver-typescript: + specifier: 3.6.1 + version: 3.6.1(@typescript-eslint/parser@6.7.5)(eslint-plugin-import@2.29.0)(eslint@8.54.0) eslint-plugin-import: specifier: 2.29.0 - version: 2.29.0(@typescript-eslint/parser@6.7.5)(eslint@8.53.0) + version: 2.29.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0) eslint-plugin-prettier: specifier: 5.0.1 - version: 5.0.1(eslint@8.53.0)(prettier@3.0.3) + version: 5.0.1(eslint@8.54.0)(prettier@3.0.3) glob: specifier: 10.3.10 version: 10.3.10 @@ -188,7 +206,10 @@ devDependencies: version: 6.0.3 vite: specifier: 4.5.0 - version: 4.5.0 + version: 4.5.0(@types/node@20.8.6) + vite-plugin-inspect: + specifier: ^0.7.42 + version: 0.7.42(rollup@4.3.0)(vite@4.5.0) vite-plugin-resolve: specifier: 2.5.1 version: 2.5.1 @@ -218,6 +239,10 @@ packages: '@jridgewell/trace-mapping': 0.3.19 dev: true + /@antfu/utils@0.7.6: + resolution: {integrity: sha512-pvFiLP2BeOKA/ZOS6jxx4XhKzdVLHDhGlFEaZ2flWWYf2xOqVniqpk38I04DFRyz+L0ASggl7SkItTc+ZLju4w==} + dev: true + /@assemblyscript/loader@0.17.14: resolution: {integrity: sha512-+PVTOfla/0XMLRTQLJFPg4u40XcdTfon6GGea70hBGi8Pd7ZymIXyVUR+vK8wt5Jb4MVKTKPIz43Myyebw5mZA==} dev: false @@ -486,6 +511,14 @@ packages: ajv-keywords: 3.5.2(ajv@6.12.6) dev: true + /@electron-toolkit/tsconfig@1.0.1(@types/node@20.8.6): + resolution: {integrity: sha512-M0Mol3odspvtCuheyujLNAW7bXq7KFNYVMRtpjFa4ZfES4MuklXBC7Nli/omvc+PRKlrklgAGx3l4VakjNo8jg==} + peerDependencies: + '@types/node': '*' + dependencies: + '@types/node': 20.8.6 + dev: false + /@electron/asar@3.2.7: resolution: {integrity: sha512-8FaSCAIiZGYFWyjeevPQt+0e9xCK9YmJ2Rjg5SXgdsXon6cRnU0Yxnbe6CvJbQn26baifur2Y2G5EBayRIsjyg==} engines: {node: '>=10.12.0'} @@ -538,6 +571,14 @@ packages: - supports-color dev: true + /@electron/remote@2.1.0(electron@27.0.4): + resolution: {integrity: sha512-38jzz2beoYTo0DNS+aoaGyLS/fHeNTAc1Aom6HlYsxKnvVWjcg4xriC7J2IUkYSEDHGKX/D7jUst+mH4dHR6QA==} + peerDependencies: + electron: '>= 13.0.0' + dependencies: + electron: 27.0.4 + dev: false + /@electron/universal@1.4.5: resolution: {integrity: sha512-3vE9WBQnvlulKylrPbyc+9M4xnD7t1JxuCOF0nrFz00XrrkgbqeqxDf90PNcjLiuB4hAZKr1JooVA6KwsXj94w==} engines: {node: '>=8.6'} @@ -751,13 +792,13 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.54.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.53.0 + eslint: 8.54.0 eslint-visitor-keys: 3.4.3 dev: true @@ -783,8 +824,8 @@ packages: - supports-color dev: true - /@eslint/js@8.53.0: - resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} + /@eslint/js@8.54.0: + resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -913,12 +954,10 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -926,7 +965,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - dev: true /@nornagon/put@0.0.8: resolution: {integrity: sha512-ugvXJjwF5ldtUpa7D95kruNJ41yFQDEKyF5CW4TgKJnh+W/zmlBzXXeKTyqIgwMFrkePN2JqOBqcF0M0oOunow==} @@ -977,6 +1015,10 @@ packages: playwright: 1.39.0 dev: true + /@polka/url@1.0.0-next.23: + resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} + dev: true + /@remusao/guess-url-type@1.2.1: resolution: {integrity: sha512-rbOqre2jW8STjheOsOaQHLgYBaBZ9Owbdt8NO7WvNZftJlaG3y/K9oOkl8ZUpuFBisIhmBuMEW6c+YrQl5inRA==} dev: false @@ -1006,6 +1048,21 @@ packages: resolution: {integrity: sha512-yvwa+aCyYI/UjeD39BnpMypG8N06l86wIDW1/PAc6ihBRnodIfZDwccxQN3n1t74wduzaz74m4ZMHZnB06567Q==} dev: false + /@rollup/pluginutils@5.0.5(rollup@4.3.0): + resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: 4.3.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 4.3.0 + dev: true + /@rollup/rollup-android-arm-eabi@4.3.0: resolution: {integrity: sha512-/4pns6BYi8MXdwnXM44yoGAcFYVHL/BYlB2q1HXZ6AzH++LaiEVWFpBWQ/glXhbMbv3E3o09igrHFbP/snhAvA==} cpu: [arm] @@ -1128,6 +1185,15 @@ packages: resolution: {integrity: sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==} dev: true + /@ts-morph/common@0.21.0: + resolution: {integrity: sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==} + dependencies: + fast-glob: 3.3.1 + minimatch: 7.4.6 + mkdirp: 2.1.6 + path-browserify: 1.0.1 + dev: false + /@types/cacheable-request@6.0.3: resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} dependencies: @@ -1157,6 +1223,10 @@ packages: - supports-color dev: true + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + /@types/filesystem@0.0.33: resolution: {integrity: sha512-2KedRPzwu2K528vFkoXnnWdsG0MtUwPjuA7pRy4vKxlxHEe8qUDZibYHXJKZZr2Cl/ELdCWYqyb/MKwsUuzBWw==} dependencies: @@ -1256,8 +1326,8 @@ packages: '@types/node': 20.8.6 optional: true - /@typescript-eslint/eslint-plugin@6.10.0(@typescript-eslint/parser@6.7.5)(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==} + /@typescript-eslint/eslint-plugin@6.12.0(@typescript-eslint/parser@6.7.5)(eslint@8.54.0)(typescript@5.2.2): + resolution: {integrity: sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -1268,13 +1338,13 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.9.1 - '@typescript-eslint/parser': 6.7.5(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.10.0 - '@typescript-eslint/type-utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.10.0 + '@typescript-eslint/parser': 6.7.5(eslint@8.54.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.12.0 + '@typescript-eslint/type-utils': 6.12.0(eslint@8.54.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.12.0 debug: 4.3.4 - eslint: 8.53.0 + eslint: 8.54.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 @@ -1285,7 +1355,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.7.5(eslint@8.53.0)(typescript@5.2.2): + /@typescript-eslint/parser@6.7.5(eslint@8.54.0)(typescript@5.2.2): resolution: {integrity: sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: @@ -1300,18 +1370,18 @@ packages: '@typescript-eslint/typescript-estree': 6.7.5(typescript@5.2.2) '@typescript-eslint/visitor-keys': 6.7.5 debug: 4.3.4 - eslint: 8.53.0 + eslint: 8.54.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.10.0: - resolution: {integrity: sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==} + /@typescript-eslint/scope-manager@6.12.0: + resolution: {integrity: sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/visitor-keys': 6.10.0 + '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/visitor-keys': 6.12.0 dev: true /@typescript-eslint/scope-manager@6.7.5: @@ -1322,8 +1392,8 @@ packages: '@typescript-eslint/visitor-keys': 6.7.5 dev: true - /@typescript-eslint/type-utils@6.10.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==} + /@typescript-eslint/type-utils@6.12.0(eslint@8.54.0)(typescript@5.2.2): + resolution: {integrity: sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1332,18 +1402,18 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.10.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.2.2) + '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.2.2) debug: 4.3.4 - eslint: 8.53.0 + eslint: 8.54.0 ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.10.0: - resolution: {integrity: sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==} + /@typescript-eslint/types@6.12.0: + resolution: {integrity: sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -1352,8 +1422,8 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2): - resolution: {integrity: sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==} + /@typescript-eslint/typescript-estree@6.12.0(typescript@5.2.2): + resolution: {integrity: sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1361,8 +1431,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/visitor-keys': 6.10.0 + '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/visitor-keys': 6.12.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -1394,30 +1464,30 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==} + /@typescript-eslint/utils@6.12.0(eslint@8.54.0)(typescript@5.2.2): + resolution: {integrity: sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.3 - '@typescript-eslint/scope-manager': 6.10.0 - '@typescript-eslint/types': 6.10.0 - '@typescript-eslint/typescript-estree': 6.10.0(typescript@5.2.2) - eslint: 8.53.0 + '@typescript-eslint/scope-manager': 6.12.0 + '@typescript-eslint/types': 6.12.0 + '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.2.2) + eslint: 8.54.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.10.0: - resolution: {integrity: sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==} + /@typescript-eslint/visitor-keys@6.12.0: + resolution: {integrity: sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.10.0 + '@typescript-eslint/types': 6.12.0 eslint-visitor-keys: 3.4.3 dev: true @@ -1451,10 +1521,22 @@ packages: requiresBuild: true dev: true + /@zeit/schemas@2.29.0: + resolution: {integrity: sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==} + dev: false + /abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + /acorn-jsx@5.3.2(acorn@8.10.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1528,6 +1610,15 @@ packages: uri-js: 4.4.1 dev: true + /ajv@8.11.0: + resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: false + /ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} dependencies: @@ -1537,6 +1628,12 @@ packages: uri-js: 4.4.1 dev: false + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + dependencies: + string-width: 4.2.3 + dev: false + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1602,6 +1699,14 @@ packages: - supports-color dev: true + /arch@2.2.0: + resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + dev: false + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: false + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1772,6 +1877,20 @@ packages: requiresBuild: true optional: true + /boxen@7.0.0: + resolution: {integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==} + engines: {node: '>=14.16'} + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.0.1 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + dev: false + /bplist-parser@0.2.0: resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} engines: {node: '>= 5.10.0'} @@ -1784,7 +1903,6 @@ packages: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -1796,7 +1914,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /browser-extension-url-match@1.0.0: resolution: {integrity: sha512-LfIs9SYgPjYksjxkgOVYZhxMIroR56isQB3YHTAmzunWuT9qrH6Fxt7TD9/s9MoKo7GP37JZbLlZhL9vwQAk3w==} @@ -1901,6 +2018,11 @@ packages: eel-wasm: 0.0.15 dev: false + /bytes@3.0.0: + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} + dev: false + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1965,10 +2087,22 @@ packages: engines: {node: '>=10'} dev: true + /camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + dev: false + /caniuse-lite@1.0.30001561: resolution: {integrity: sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==} dev: true + /chalk-template@0.4.0: + resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} + engines: {node: '>=12'} + dependencies: + chalk: 4.1.2 + dev: false + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1984,7 +2118,11 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: true + + /chalk@5.0.1: + resolution: {integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} @@ -2010,6 +2148,11 @@ packages: escape-string-regexp: 5.0.0 dev: true + /cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + dev: false + /cli-truncate@2.1.0: resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} engines: {node: '>=8'} @@ -2020,6 +2163,15 @@ packages: dev: true optional: true + /clipboardy@3.0.0: + resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + arch: 2.2.0 + execa: 5.1.1 + is-wsl: 2.2.0 + dev: false + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2034,6 +2186,10 @@ packages: dependencies: mimic-response: 1.0.1 + /code-block-writer@12.0.0: + resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + dev: false + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -2075,9 +2231,30 @@ packages: engines: {node: '>=0.10.0'} dev: true + /compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /compression@1.7.4: + resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + engines: {node: '>= 0.8.0'} + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true /conf@10.2.0: resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} @@ -2102,6 +2279,11 @@ packages: typescript: 4.9.5 dev: true + /content-disposition@0.5.2: + resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} + engines: {node: '>= 0.6'} + dev: false + /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true @@ -2191,6 +2373,17 @@ packages: mimic-fn: 3.1.0 dev: false + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2260,6 +2453,11 @@ packages: which-typed-array: 1.1.11 dev: false + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: false + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true @@ -2615,7 +2813,7 @@ packages: esbuild: 0.18.20 magic-string: 0.30.5 picocolors: 1.0.0 - vite: 4.5.0 + vite: 4.5.0(@types/node@20.8.6) transitivePeerDependencies: - supports-color dev: true @@ -2650,6 +2848,14 @@ packages: dependencies: once: 1.4.0 + /enhanced-resolve@5.15.0: + resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + engines: {node: '>=10.13.0'} + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + dev: true + /ensure-error@2.1.0: resolution: {integrity: sha512-+BMSJHw9gxiJAAp2ZR1E0TNcL09dD3lOvkl7WVm4+Y6xnes/pMetP/TzCHiDduh8ihNDjbGfuYxl7l4PA1xZ8A==} engines: {node: '>=8'} @@ -2673,6 +2879,10 @@ packages: is-arrayish: 0.2.1 dev: true + /error-stack-parser-es@0.1.1: + resolution: {integrity: sha512-g/9rfnvnagiNf+DRMHEVGuGuIBlCIMDFoTA616HaP2l9PlCjGjVhD98PNbVSJvmK4TttqT5mV5tInMhoFgi+aA==} + dev: true + /es-abstract@1.22.2: resolution: {integrity: sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==} engines: {node: '>= 0.4'} @@ -2810,6 +3020,17 @@ packages: engines: {node: '>=12'} dev: true + /eslint-import-resolver-exports@1.0.0-beta.5(eslint-plugin-import@2.29.0)(eslint@8.54.0): + resolution: {integrity: sha512-o6t0w7muUpXr7MkUVzD5igQoDfAQvTmcPp8HEAJdNF8eOuAO+yn6I/TTyMxz9ecCwzX7e02vzlkHURoScUuidg==} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + dependencies: + eslint: 8.54.0 + eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0) + resolve.exports: 2.0.2 + dev: true + /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: @@ -2820,7 +3041,30 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0): + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.5)(eslint-plugin-import@2.29.0)(eslint@8.54.0): + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + dependencies: + debug: 4.3.4 + enhanced-resolve: 5.15.0 + eslint: 8.54.0 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0) + eslint-plugin-import: 2.29.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0) + fast-glob: 3.3.1 + get-tsconfig: 4.7.2 + is-core-module: 2.13.1 + is-glob: 4.0.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -2841,15 +3085,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.7.5(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.54.0)(typescript@5.2.2) debug: 3.2.7 - eslint: 8.53.0 + eslint: 8.54.0 eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.5)(eslint-plugin-import@2.29.0)(eslint@8.54.0) transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.7.5)(eslint@8.53.0): + /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0): resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==} engines: {node: '>=4'} peerDependencies: @@ -2859,16 +3104,16 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.7.5(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.7.5(eslint@8.54.0)(typescript@5.2.2) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.53.0 + eslint: 8.54.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.5)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.54.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -2884,7 +3129,7 @@ packages: - supports-color dev: true - /eslint-plugin-prettier@5.0.1(eslint@8.53.0)(prettier@3.0.3): + /eslint-plugin-prettier@5.0.1(eslint@8.54.0)(prettier@3.0.3): resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -2898,7 +3143,7 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.53.0 + eslint: 8.54.0 prettier: 3.0.3 prettier-linter-helpers: 1.0.0 synckit: 0.8.5 @@ -2917,15 +3162,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.53.0: - resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} + /eslint@8.54.0: + resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0) '@eslint-community/regexpp': 4.9.1 '@eslint/eslintrc': 2.1.3 - '@eslint/js': 8.53.0 + '@eslint/js': 8.54.0 '@humanwhocodes/config-array': 0.11.13 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -2992,6 +3237,10 @@ packages: engines: {node: '>=4.0'} dev: true + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -3022,7 +3271,6 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - dev: true /execa@7.2.0: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} @@ -3092,7 +3340,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -3102,11 +3349,16 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fast-url-parser@1.1.3: + resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} + dependencies: + punycode: 1.4.1 + dev: false + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 - dev: true /fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -3157,7 +3409,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} @@ -3236,6 +3487,15 @@ packages: jsonfile: 6.1.0 universalify: 2.0.0 + /fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + /fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} @@ -3329,7 +3589,6 @@ packages: /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: true /get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} @@ -3339,12 +3598,17 @@ packages: get-intrinsic: 1.2.1 dev: true + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} @@ -3473,7 +3737,6 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} @@ -3598,7 +3861,6 @@ packages: /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - dev: true /human-signals@4.3.1: resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} @@ -3676,6 +3938,10 @@ packages: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: false + /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} @@ -3751,7 +4017,6 @@ packages: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true - dev: true /is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} @@ -3762,7 +4027,6 @@ packages: /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -3773,7 +4037,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} @@ -3804,7 +4067,6 @@ packages: /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} @@ -3831,6 +4093,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-port-reachable@4.0.0: + resolution: {integrity: sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -3850,7 +4117,6 @@ packages: /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - dev: true /is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} @@ -3901,7 +4167,6 @@ packages: engines: {node: '>=8'} dependencies: is-docker: 2.2.1 - dev: true /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -4207,12 +4472,10 @@ packages: /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} @@ -4220,12 +4483,23 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 - dev: true + + /mime-db@1.33.0: + resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} + engines: {node: '>= 0.6'} + dev: false /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + /mime-types@2.1.18: + resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.33.0 + dev: false + /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -4269,7 +4543,6 @@ packages: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 - dev: true /minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} @@ -4278,6 +4551,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: false + /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -4295,7 +4575,6 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true /minipass-collect@1.0.2: resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} @@ -4364,6 +4643,21 @@ packages: engines: {node: '>=10'} hasBin: true + /mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} + engines: {node: '>=10'} + hasBin: true + dev: false + + /mrmime@1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + dev: true + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -4476,7 +4770,6 @@ packages: engines: {node: '>=8'} dependencies: path-key: 3.1.1 - dev: true /npm-run-path@5.1.0: resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} @@ -4542,6 +4835,11 @@ packages: es-abstract: 1.22.2 dev: true + /on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -4665,6 +4963,10 @@ packages: peberminta: 0.9.0 dev: false + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: false + /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -4680,6 +4982,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /path-is-inside@1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + dev: false + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -4700,6 +5006,10 @@ packages: lru-cache: 10.0.1 minipass: 7.0.4 + /path-to-regexp@2.2.1: + resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==} + dev: false + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4725,7 +5035,6 @@ packages: /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /pkg-up@3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} @@ -4815,18 +5124,36 @@ packages: end-of-stream: 1.4.4 once: 1.4.0 + /punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + dev: false + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true /quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} + /range-parser@1.2.0: + resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} + engines: {node: '>= 0.6'} + dev: false + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: false + /read-config-file@6.3.2: resolution: {integrity: sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==} engines: {node: '>=12.0.0'} @@ -4894,6 +5221,20 @@ packages: define-properties: 1.2.1 set-function-name: 2.0.1 + /registry-auth-token@3.3.2: + resolution: {integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==} + dependencies: + rc: 1.2.8 + safe-buffer: 5.2.1 + dev: false + + /registry-url@3.1.0: + resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==} + engines: {node: '>=0.10.0'} + dependencies: + rc: 1.2.8 + dev: false + /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4912,6 +5253,15 @@ packages: engines: {node: '>=4'} dev: true + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -4933,7 +5283,6 @@ packages: /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -4986,7 +5335,6 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true /safe-array-concat@1.0.1: resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} @@ -5000,7 +5348,6 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -5068,6 +5415,39 @@ packages: type-fest: 0.20.2 dev: false + /serve-handler@6.1.5: + resolution: {integrity: sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==} + dependencies: + bytes: 3.0.0 + content-disposition: 0.5.2 + fast-url-parser: 1.1.3 + mime-types: 2.1.18 + minimatch: 3.1.2 + path-is-inside: 1.0.2 + path-to-regexp: 2.2.1 + range-parser: 1.2.0 + dev: false + + /serve@14.2.1: + resolution: {integrity: sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA==} + engines: {node: '>= 14'} + hasBin: true + dependencies: + '@zeit/schemas': 2.29.0 + ajv: 8.11.0 + arg: 5.0.2 + boxen: 7.0.0 + chalk: 5.0.1 + chalk-template: 0.4.0 + clipboardy: 3.0.0 + compression: 1.7.4 + is-port-reachable: 4.0.0 + serve-handler: 6.1.5 + update-check: 1.5.4 + transitivePeerDependencies: + - supports-color + dev: false + /set-function-name@2.0.1: resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} engines: {node: '>= 0.4'} @@ -5099,7 +5479,6 @@ packages: /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true /signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} @@ -5112,6 +5491,15 @@ packages: semver: 7.5.4 dev: true + /sirv@2.0.3: + resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.23 + mrmime: 1.0.1 + totalist: 3.0.1 + dev: true + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -5293,7 +5681,6 @@ packages: /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - dev: true /strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} @@ -5307,6 +5694,11 @@ packages: min-indent: 1.0.1 dev: true + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: false + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -5332,7 +5724,6 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - dev: true /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -5347,6 +5738,11 @@ packages: tslib: 2.6.2 dev: true + /tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + dev: true + /tar@6.2.0: resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} engines: {node: '>=10'} @@ -5415,6 +5811,10 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 + + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} dev: true /trim-newlines@4.1.1: @@ -5437,6 +5837,13 @@ packages: typescript: 5.2.2 dev: true + /ts-morph@20.0.0: + resolution: {integrity: sha512-JVmEJy2Wow5n/84I3igthL9sudQ8qzjh/6i4tmYCm6IqYyKFlNbJZi7oBdjyqcWSWYRu3CtL0xbT6fS03ESZIg==} + dependencies: + '@ts-morph/common': 0.21.0 + code-block-writer: 12.0.0 + dev: false + /tsconfig-paths@3.14.2: resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} dependencies: @@ -5590,6 +5997,13 @@ packages: picocolors: 1.0.0 dev: true + /update-check@1.5.4: + resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} + dependencies: + registry-auth-token: 3.3.2 + registry-url: 3.1.0 + dev: false + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -5634,6 +6048,11 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + /verror@1.10.1: resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} engines: {node: '>=0.6.0'} @@ -5645,13 +6064,37 @@ packages: dev: true optional: true + /vite-plugin-inspect@0.7.42(rollup@4.3.0)(vite@4.5.0): + resolution: {integrity: sha512-JCyX86wr3siQc+p9Kd0t8VkFHAJag0RaQVIpdFGSv5FEaePEVB6+V/RGtz2dQkkGSXQzRWrPs4cU3dRKg32bXw==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': '*' + vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + dependencies: + '@antfu/utils': 0.7.6 + '@rollup/pluginutils': 5.0.5(rollup@4.3.0) + debug: 4.3.4 + error-stack-parser-es: 0.1.1 + fs-extra: 11.1.1 + open: 9.1.0 + picocolors: 1.0.0 + sirv: 2.0.3 + vite: 4.5.0(@types/node@20.8.6) + transitivePeerDependencies: + - rollup + - supports-color + dev: true + /vite-plugin-resolve@2.5.1: resolution: {integrity: sha512-9dD0Yq5JT1RxHQGZOyhC7e/JlhyhMCftCpQ8TPzQa7KEB/3ERnoCPinH3VJk/0C8qHsA+l41bIcHh5BcHBTmAw==} dependencies: lib-esm: 0.4.1 dev: true - /vite@4.5.0: + /vite@4.5.0(@types/node@20.8.6): resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -5679,6 +6122,7 @@ packages: terser: optional: true dependencies: + '@types/node': 20.8.6 esbuild: 0.18.20 postcss: 8.4.31 rollup: 4.3.0 @@ -5737,6 +6181,13 @@ packages: dependencies: isexe: 3.1.1 + /widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + dev: false + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} diff --git a/src/config/index.ts b/src/config/index.ts index f8bdff14..4b66e976 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -2,10 +2,11 @@ import Store from 'electron-store'; import { deepmerge } from 'deepmerge-ts'; import defaultConfig from './defaults'; -import plugins from './plugins'; -import store from './store'; -import { restart } from '../providers/app-controls'; +import store from './store'; +import plugins from './plugins'; + +import { restart } from '@/providers/app-controls'; const set = (key: string, value: unknown) => { store.set(key, value); @@ -15,7 +16,7 @@ const setPartial = (key: string, value: object) => { store.set(key, newValue); }; -function setMenuOption(key: string, value: unknown) { +function setMenuOption(key: string, value: unknown) { set(key, value); if (store.get('options.restartOnConfigChanges')) { restart(); @@ -24,24 +25,55 @@ function setMenuOption(key: string, value: unknown) { // MAGIC OF TYPESCRIPT -type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]] -type Join = K extends string | number ? - P extends string | number ? - `${K}${'' extends P ? '' : '.'}${P}` - : never : never; -type Paths = [D] extends [never] ? never : T extends object ? - { [K in keyof T]-?: K extends string | number ? - `${K}` | Join> +type Prev = [ + never, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + ...0[], +]; +type Join = K extends string | number + ? P extends string | number + ? `${K}${'' extends P ? '' : '.'}${P}` : never - }[keyof T] : '' + : never; +type Paths = [D] extends [never] + ? never + : T extends object + ? { + [K in keyof T]-?: K extends string | number + ? `${K}` | Join> + : never; + }[keyof T] + : ''; type SplitKey = K extends `${infer A}.${infer B}` ? [A, B] : [K, string]; -type PathValue = - SplitKey extends [infer A extends keyof T, infer B extends string] - ? PathValue - : T; -const get = >(key: Key) => store.get(key) as PathValue; +type PathValue = SplitKey extends [ + infer A extends keyof T, + infer B extends string, +] + ? PathValue + : T; +const get = >(key: Key) => + store.get(key) as PathValue; export default { defaultConfig, diff --git a/src/config/plugins.ts b/src/config/plugins.ts index 32b516ae..afa494f5 100644 --- a/src/config/plugins.ts +++ b/src/config/plugins.ts @@ -1,16 +1,16 @@ import store from './store'; -import { restart } from '../providers/app-controls'; +import { restart } from '@/providers/app-controls'; -import type { PluginBaseConfig } from '../plugins/utils/builder'; +import type { PluginConfig } from '@/types/plugins'; export function getPlugins() { - return store.get('plugins') as Record; + return store.get('plugins') as Record; } export function isEnabled(plugin: string) { - const pluginConfig = (store.get('plugins') as Record)[plugin]; + const pluginConfig = (store.get('plugins') as Record)[plugin]; return pluginConfig !== undefined && pluginConfig.enabled; } diff --git a/src/config/store.ts b/src/config/store.ts index 80b6c8ff..b70d9562 100644 --- a/src/config/store.ts +++ b/src/config/store.ts @@ -1,15 +1,18 @@ import Store from 'electron-store'; import Conf from 'conf'; -import { pluginBuilders } from 'virtual:PluginBuilders'; +import { allPlugins } from 'virtual:plugins'; import defaults from './defaults'; -import { DefaultPresetList, type Preset } from '../plugins/downloader/types'; +import { DefaultPresetList, type Preset } from '@/plugins/downloader/types'; -const setDefaultPluginOptions = (store: Conf>, plugin: keyof typeof pluginBuilders) => { +const setDefaultPluginOptions = ( + store: Conf>, + plugin: string, +) => { if (!store.get(`plugins.${plugin}`)) { - store.set(`plugins.${plugin}`, pluginBuilders[plugin].config); + store.set(`plugins.${plugin}`, allPlugins[plugin].config); } }; @@ -22,19 +25,24 @@ const migrations = { } }, '>=2.1.0'(store: Conf>) { - const originalPreset = store.get('plugins.downloader.preset') as string | undefined; + const originalPreset = store.get('plugins.downloader.preset') as + | string + | undefined; if (originalPreset) { if (originalPreset !== 'opus') { store.set('plugins.downloader.selectedPreset', 'Custom'); store.set('plugins.downloader.customPresetSetting', { extension: 'mp3', - ffmpegArgs: store.get('plugins.downloader.ffmpegArgs') as string[] ?? DefaultPresetList['mp3 (256kbps)'].ffmpegArgs, + ffmpegArgs: + (store.get('plugins.downloader.ffmpegArgs') as string[]) ?? + DefaultPresetList['mp3 (256kbps)'].ffmpegArgs, } satisfies Preset); } else { store.set('plugins.downloader.selectedPreset', 'Source'); store.set('plugins.downloader.customPresetSetting', { extension: null, - ffmpegArgs: store.get('plugins.downloader.ffmpegArgs') as string[] ?? [], + ffmpegArgs: + (store.get('plugins.downloader.ffmpegArgs') as string[]) ?? [], } satisfies Preset); } store.delete('plugins.downloader.preset'); @@ -47,7 +55,7 @@ const migrations = { if (store.get('plugins.notifications.toastStyle') === undefined) { const pluginOptions = store.get('plugins.notifications') || {}; store.set('plugins.notifications', { - ...pluginBuilders.notifications.config, + ...allPlugins.notifications.config, ...pluginOptions, }); } @@ -82,10 +90,14 @@ const migrations = { } }, '>=1.12.0'(store: Conf>) { - const options = store.get('plugins.shortcuts') as Record>; + const options = store.get('plugins.shortcuts') as Record< + string, + | { + action: string; + shortcut: unknown; + }[] + | Record + >; let updated = false; for (const optionType of ['global', 'local']) { if (Array.isArray(options[optionType])) { @@ -151,12 +163,7 @@ const migrations = { export default new Store({ defaults: { ...defaults, - plugins: Object - .entries(pluginBuilders) - .reduce((prev, [id, builder]) => ({ - ...prev, - [id]: (builder as PluginBuilderList[keyof PluginBuilderList]).config, - }), {}), + // README: 'plugin' uses deepmerge to populate the default values, so it is not necessary to include it here }, clearInvalidConfig: false, migrations, diff --git a/src/index.ts b/src/index.ts index e665c975..3a6b0be2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,19 @@ import path from 'node:path'; import url from 'node:url'; import fs from 'node:fs'; -import { BrowserWindow, app, screen, globalShortcut, session, shell, dialog, ipcMain } from 'electron'; -import enhanceWebRequest, { BetterSession } from '@jellybrick/electron-better-web-request'; +import { + BrowserWindow, + app, + screen, + globalShortcut, + session, + shell, + dialog, + ipcMain, +} from 'electron'; +import enhanceWebRequest, { + BetterSession, +} from '@jellybrick/electron-better-web-request'; import is from 'electron-is'; import unhandled from 'electron-unhandled'; import { autoUpdater } from 'electron-updater'; @@ -12,30 +23,32 @@ import { parse } from 'node-html-parser'; import { deepmerge } from 'deepmerge-ts'; import { deepEqual } from 'fast-equals'; -import { mainPlugins } from 'virtual:MainPlugins'; -import { pluginBuilders } from 'virtual:PluginBuilders'; +import { allPlugins, mainPlugins } from 'virtual:plugins'; -import config from './config'; +import config from '@/config'; -import { refreshMenu, setApplicationMenu } from './menu'; -import { fileExists, injectCSS, injectCSSAsFile } from './plugins/utils/main'; -import { isTesting } from './utils/testing'; -import { setUpTray } from './tray'; -import { setupSongInfo } from './providers/song-info'; -import { restart, setupAppControls } from './providers/app-controls'; -import { APP_PROTOCOL, handleProtocol, setupProtocolHandler } from './providers/protocol-handler'; +import { refreshMenu, setApplicationMenu } from '@/menu'; +import { fileExists, injectCSS, injectCSSAsFile } from '@/plugins/utils/main'; +import { isTesting } from '@/utils/testing'; +import { setUpTray } from '@/tray'; +import { setupSongInfo } from '@/providers/song-info'; +import { restart, setupAppControls } from '@/providers/app-controls'; +import { + APP_PROTOCOL, + handleProtocol, + setupProtocolHandler, +} from '@/providers/protocol-handler'; - -import youtubeMusicCSS from './youtube-music.css?inline'; +import youtubeMusicCSS from '@/youtube-music.css?inline'; import { forceLoadMainPlugin, forceUnloadMainPlugin, getAllLoadedMainPlugins, loadAllMainPlugins, - registerMainPlugin -} from './loader/main'; -import { MainPluginFactory, PluginBaseConfig, PluginBuilder } from './plugins/utils/builder'; +} from '@/loader/main'; + +import type { PluginConfig } from '@/types/plugins'; // Catch errors and log them unhandled({ @@ -57,7 +70,10 @@ if (!gotTheLock) { // SharedArrayBuffer: Required for downloader (@ffmpeg/core-mt) // OverlayScrollbar: Required for overlay scrollbars -app.commandLine.appendSwitch('enable-features', 'OverlayScrollbar,SharedArrayBuffer'); +app.commandLine.appendSwitch( + 'enable-features', + 'OverlayScrollbar,SharedArrayBuffer', +); if (config.get('options.disableHardwareAcceleration')) { if (is.dev()) { console.log('Disabling hardware acceleration'); @@ -95,42 +111,59 @@ function onClosed() { ipcMain.handle('get-main-plugin-names', () => Object.keys(mainPlugins)); - const initHook = (win: BrowserWindow) => { - ipcMain.handle('get-config', (_, id: keyof PluginBuilderList) => deepmerge(pluginBuilders[id].config, config.get(`plugins.${id}`) ?? {}) as PluginBuilderList[typeof id]['config']); - ipcMain.handle('set-config', (_, name: string, obj: object) => config.setPartial(`plugins.${name}`, obj)); + ipcMain.handle( + 'get-config', + (_, id: string) => + deepmerge( + allPlugins[id].config, + config.get(`plugins.${id}`) ?? {}, + ) as PluginConfig, + ); + ipcMain.handle('set-config', (_, name: string, obj: object) => + config.setPartial(`plugins.${name}`, obj), + ); config.watch((newValue, oldValue) => { - const newPluginConfigList = (newValue?.plugins ?? {}) as Record; - const oldPluginConfigList = (oldValue?.plugins ?? {}) as Record; + const newPluginConfigList = (newValue?.plugins ?? {}) as Record< + string, + unknown + >; + const oldPluginConfigList = (oldValue?.plugins ?? {}) as Record< + string, + unknown + >; Object.entries(newPluginConfigList).forEach(([id, newPluginConfig]) => { const isEqual = deepEqual(oldPluginConfigList[id], newPluginConfig); if (!isEqual) { - const oldConfig = oldPluginConfigList[id] as PluginBaseConfig; - const config = deepmerge(pluginBuilders[id as keyof PluginBuilderList].config, newPluginConfig) as PluginBaseConfig; + const oldConfig = oldPluginConfigList[id] as PluginConfig; + const config = deepmerge( + allPlugins[id].config, + newPluginConfig, + ) as PluginConfig; if (config.enabled !== oldConfig?.enabled) { if (config.enabled) { win.webContents.send('plugin:enable', id); ipcMain.emit('plugin:enable', id); - forceLoadMainPlugin(id as keyof PluginBuilderList, win); + forceLoadMainPlugin(id, win); } else { win.webContents.send('plugin:unload', id); ipcMain.emit('plugin:unload', id); - forceUnloadMainPlugin(id as keyof PluginBuilderList, win); + forceUnloadMainPlugin(id, win); } - if (pluginBuilders[id as keyof PluginBuilderList].restartNeeded) { - showNeedToRestartDialog(id as keyof PluginBuilderList); + if (mainPlugins[id]?.restartNeeded) { + showNeedToRestartDialog(id); } } const mainPlugin = getAllLoadedMainPlugins()[id]; if (mainPlugin) { - if (config.enabled) { - mainPlugin.onConfigChange?.(config); + if (config.enabled && typeof mainPlugin.backend !== 'function') { + mainPlugin.backend?.onConfigChange?.bind(mainPlugin.backend)?.(config); } } @@ -140,14 +173,15 @@ const initHook = (win: BrowserWindow) => { }); }; -const showNeedToRestartDialog = (id: keyof PluginBuilderList) => { - const builder = pluginBuilders[id]; +const showNeedToRestartDialog = (id: string) => { + const plugin = mainPlugins[id]; + const dialogOptions: Electron.MessageBoxOptions = { type: 'info', buttons: ['Restart Now', 'Later'], title: 'Restart Required', - message: `"${builder.name ?? builder.id}" needs to restart`, - detail: `"${builder.name ?? builder.id}" plugin requires a restart to take effect`, + message: `"${plugin.name ?? id}" needs to restart`, + detail: `"${plugin.name ?? id}" plugin requires a restart to take effect`, defaultId: 0, cancelId: 1, }; @@ -186,7 +220,10 @@ function initTheme(win: BrowserWindow) { injectCSSAsFile(win.webContents, cssFile); }, () => { - console.warn('[YTMusic]', `CSS file "${cssFile}" does not exist, ignoring`); + console.warn( + '[YTMusic]', + `CSS file "${cssFile}" does not exist, ignoring`, + ); }, ); } @@ -224,46 +261,43 @@ async function createMainWindow() { ...(isTesting() ? undefined : { - // Sandbox is only enabled in tests for now - // See https://www.electronjs.org/docs/latest/tutorial/sandbox#preload-scripts - sandbox: false, - }), + // Sandbox is only enabled in tests for now + // See https://www.electronjs.org/docs/latest/tutorial/sandbox#preload-scripts + sandbox: false, + }), }, frame: !is.macOS() && !useInlineMenu, titleBarOverlay: defaultTitleBarOverlayOptions, titleBarStyle: useInlineMenu ? 'hidden' - : (is.macOS() - ? 'hiddenInset' - : 'default'), + : is.macOS() + ? 'hiddenInset' + : 'default', autoHideMenuBar: config.get('options.hideMenu'), }); initHook(win); initTheme(win); - Object.entries(pluginBuilders).forEach(([id, builder]) => { - const typedBuilder = builder as PluginBuilder; - const plugin = mainPlugins[id] as MainPluginFactory | undefined; - - registerMainPlugin(id, typedBuilder, plugin); - }); await loadAllMainPlugins(win); if (windowPosition) { const { x: windowX, y: windowY } = windowPosition; const winSize = win.getSize(); - const displaySize - = screen.getDisplayNearestPoint(windowPosition).bounds; + const displaySize = screen.getDisplayNearestPoint(windowPosition).bounds; if ( - windowX + winSize[0] < displaySize.x - 8 - || windowX - winSize[0] > displaySize.x + displaySize.width - || windowY < displaySize.y - 8 - || windowY > displaySize.y + displaySize.height + windowX + winSize[0] < displaySize.x - 8 || + windowX - winSize[0] > displaySize.x + displaySize.width || + windowY < displaySize.y - 8 || + windowY > displaySize.y + displaySize.height ) { // Window is offscreen if (is.dev()) { console.log( - `Window tried to render offscreen, windowSize=${String(winSize)}, displaySize=${String(displaySize)}, position=${String(windowPosition)}`, + `Window tried to render offscreen, windowSize=${String( + winSize, + )}, displaySize=${String(displaySize)}, position=${String( + windowPosition, + )}`, ); } } else { @@ -316,7 +350,11 @@ async function createMainWindow() { const savedTimeouts: Record = {}; - function lateSave(key: string, value: unknown, fn: (key: string, value: unknown) => void = config.set) { + function lateSave( + key: string, + value: unknown, + fn: (key: string, value: unknown) => void = config.set, + ) { if (savedTimeouts[key]) { clearTimeout(savedTimeouts[key]); } @@ -327,7 +365,7 @@ async function createMainWindow() { }, 600); } - app.on('render-process-gone', (event, webContents, details) => { + app.on('render-process-gone', (_event, _webContents, details) => { showUnresponsiveDialog(win, details); }); @@ -343,7 +381,10 @@ async function createMainWindow() { if (useInlineMenu) { win.setTitleBarOverlay({ ...defaultTitleBarOverlayOptions, - height: Math.floor(defaultTitleBarOverlayOptions.height! * win.webContents.getZoomFactor()), + height: Math.floor( + defaultTitleBarOverlayOptions.height! * + win.webContents.getZoomFactor(), + ), }); } @@ -365,14 +406,25 @@ async function createMainWindow() { `); } else { const rendererPath = path.join(__dirname, '..', 'renderer'); - const indexHTML = parse(fs.readFileSync(path.join(rendererPath, 'index.html'), 'utf-8')); + const indexHTML = parse( + fs.readFileSync(path.join(rendererPath, 'index.html'), 'utf-8'), + ); const scriptSrc = indexHTML.querySelector('script')!; - const scriptPath = path.join(rendererPath, scriptSrc.getAttribute('src')!); + const scriptPath = path.join( + rendererPath, + scriptSrc.getAttribute('src')!, + ); const scriptString = fs.readFileSync(scriptPath, 'utf-8'); - await win.webContents.executeJavaScriptInIsolatedWorld(0, [{ - code: scriptString + ';0', - url: url.pathToFileURL(scriptPath).toString(), - }], true); + await win.webContents.executeJavaScriptInIsolatedWorld( + 0, + [ + { + code: scriptString + ';0', + url: url.pathToFileURL(scriptPath).toString(), + }, + ], + true, + ); } }); @@ -381,27 +433,32 @@ async function createMainWindow() { return win; } -app.once('browser-window-created', (event, win) => { +app.once('browser-window-created', (_event, win) => { if (config.get('options.overrideUserAgent')) { // User agents are from https://developers.whatismybrowser.com/useragents/explore/ const originalUserAgent = win.webContents.userAgent; const userAgents = { mac: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12.1; rv:95.0) Gecko/20100101 Firefox/95.0', - windows: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0', + windows: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0', linux: 'Mozilla/5.0 (Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0', }; - const updatedUserAgent - = is.macOS() ? userAgents.mac - : (is.windows() ? userAgents.windows - : userAgents.linux); + const updatedUserAgent = is.macOS() + ? userAgents.mac + : is.windows() + ? userAgents.windows + : userAgents.linux; win.webContents.userAgent = updatedUserAgent; app.userAgentFallback = updatedUserAgent; win.webContents.session.webRequest.onBeforeSendHeaders((details, cb) => { // This will only happen if login failed, and "retry" was pressed - if (win.webContents.getURL().startsWith('https://accounts.google.com') && details.url.startsWith('https://accounts.google.com')) { + if ( + win.webContents.getURL().startsWith('https://accounts.google.com') && + details.url.startsWith('https://accounts.google.com') + ) { details.requestHeaders['User-Agent'] = originalUserAgent; } @@ -412,33 +469,41 @@ app.once('browser-window-created', (event, win) => { setupSongInfo(win); setupAppControls(); - win.webContents.on('did-fail-load', ( - _event, - errorCode, - errorDescription, - validatedURL, - isMainFrame, - frameProcessId, - frameRoutingId, - ) => { - const log = JSON.stringify({ - error: 'did-fail-load', + win.webContents.on( + 'did-fail-load', + ( + _event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId, - }, null, '\t'); - if (is.dev()) { - console.log(log); - } + ) => { + const log = JSON.stringify( + { + error: 'did-fail-load', + errorCode, + errorDescription, + validatedURL, + isMainFrame, + frameProcessId, + frameRoutingId, + }, + null, + '\t', + ); + if (is.dev()) { + console.log(log); + } - if (errorCode !== -3) { // -3 is a false positive - win.webContents.send('log', log); - win.webContents.loadFile(path.join(__dirname, 'error.html')); - } - }); + if (errorCode !== -3) { + // -3 is a false positive + win.webContents.send('log', log); + win.webContents.loadFile(path.join(__dirname, 'error.html')); + } + }, + ); win.webContents.on('will-prevent-unload', (event) => { event.preventDefault(); @@ -484,17 +549,29 @@ app.on('ready', async () => { const appLocation = process.execPath; const appData = app.getPath('appData'); // Check shortcut validity if not in dev mode / running portable app - if (!is.dev() && !appLocation.startsWith(path.join(appData, '..', 'Local', 'Temp'))) { - const shortcutPath = path.join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'YouTube Music.lnk'); - try { // Check if shortcut is registered and valid + if ( + !is.dev() && + !appLocation.startsWith(path.join(appData, '..', 'Local', 'Temp')) + ) { + const shortcutPath = path.join( + appData, + 'Microsoft', + 'Windows', + 'Start Menu', + 'Programs', + 'YouTube Music.lnk', + ); + try { + // Check if shortcut is registered and valid const shortcutDetails = shell.readShortcutLink(shortcutPath); // Throw error if doesn't exist yet if ( - shortcutDetails.target !== appLocation - || shortcutDetails.appUserModelId !== appID + shortcutDetails.target !== appLocation || + shortcutDetails.appUserModelId !== appID ) { throw 'needUpdate'; } - } catch (error) { // If not valid -> Register shortcut + } catch (error) { + // If not valid -> Register shortcut shell.writeShortcutLink( shortcutPath, error === 'needUpdate' ? 'update' : 'create', @@ -556,8 +633,8 @@ app.on('ready', async () => { clearTimeout(updateTimeout); }, 2000); autoUpdater.on('update-available', () => { - const downloadLink - = 'https://github.com/th-ch/youtube-music/releases/latest'; + const downloadLink = + 'https://github.com/th-ch/youtube-music/releases/latest'; const dialogOptions: Electron.MessageBoxOptions = { type: 'info', buttons: ['OK', 'Download', 'Disable updates'], @@ -597,8 +674,10 @@ app.on('ready', async () => { if (config.get('options.hideMenu') && !config.get('options.hideMenuWarned')) { dialog.showMessageBox(mainWindow, { - type: 'info', title: 'Hide Menu Enabled', - message: "Menu is hidden, use 'Alt' to show it (or 'Escape' if using in-app-menu)", + type: 'info', + title: 'Hide Menu Enabled', + message: + "Menu is hidden, use 'Alt' to show it (or 'Escape' if using in-app-menu)", }); config.set('options.hideMenuWarned', true); } @@ -624,31 +703,36 @@ app.on('ready', async () => { } }); -function showUnresponsiveDialog(win: BrowserWindow, details: Electron.RenderProcessGoneDetails) { +function showUnresponsiveDialog( + win: BrowserWindow, + details: Electron.RenderProcessGoneDetails, +) { if (details) { console.log('Unresponsive Error!\n' + JSON.stringify(details, null, '\t')); } - dialog.showMessageBox(win, { - type: 'error', - title: 'Window Unresponsive', - message: 'The Application is Unresponsive', - detail: 'We are sorry for the inconvenience! please choose what to do:', - buttons: ['Wait', 'Relaunch', 'Quit'], - cancelId: 0, - }).then((result) => { - switch (result.response) { - case 1: { - restart(); - break; - } + dialog + .showMessageBox(win, { + type: 'error', + title: 'Window Unresponsive', + message: 'The Application is Unresponsive', + detail: 'We are sorry for the inconvenience! please choose what to do:', + buttons: ['Wait', 'Relaunch', 'Quit'], + cancelId: 0, + }) + .then((result) => { + switch (result.response) { + case 1: { + restart(); + break; + } - case 2: { - app.quit(); - break; + case 2: { + app.quit(); + break; + } } - } - }); + }); } function removeContentSecurityPolicy( @@ -671,18 +755,21 @@ function removeContentSecurityPolicy( }); // When multiple listeners are defined, apply them all - betterSession.webRequest.setResolver('onHeadersReceived', async (listeners) => { - return listeners.reduce( - async (accumulator, listener) => { - const acc = await accumulator; - if (acc.cancel) { - return acc; - } + betterSession.webRequest.setResolver( + 'onHeadersReceived', + async (listeners) => { + return listeners.reduce( + async (accumulator, listener) => { + const acc = await accumulator; + if (acc.cancel) { + return acc; + } - const result = await listener.apply(); - return { ...accumulator, ...result }; - }, - Promise.resolve({ cancel: false }), - ); - }); + const result = await listener.apply(); + return { ...accumulator, ...result }; + }, + Promise.resolve({ cancel: false }), + ); + }, + ); } diff --git a/src/loader/main.ts b/src/loader/main.ts index 883feead..18780a5c 100644 --- a/src/loader/main.ts +++ b/src/loader/main.ts @@ -1,128 +1,138 @@ import { BrowserWindow, ipcMain } from 'electron'; import { deepmerge } from 'deepmerge-ts'; +import { mainPlugins } from 'virtual:plugins'; -import config from '../config'; -import { injectCSS } from '../plugins/utils/main'; -import { - MainPlugin, - MainPluginContext, - MainPluginFactory, - PluginBaseConfig, - PluginBuilder -} from '../plugins/utils/builder'; +import config from '@/config'; +import { startPlugin, stopPlugin } from '@/utils'; -const allPluginFactoryList: Record> = {}; -const allPluginBuilders: Record> = {}; -const unregisterStyleMap: Record void)[]> = {}; -const loadedPluginMap: Record> = {}; +import type { PluginConfig, PluginDef } from '@/types/plugins'; +import type { BackendContext } from '@/types/contexts'; -const createContext = < - Key extends keyof PluginBuilderList, - Config extends PluginBaseConfig = PluginBuilderList[Key]['config'], ->(id: Key, win: BrowserWindow): MainPluginContext => ({ - getConfig: () => deepmerge(allPluginBuilders[id].config, config.get(`plugins.${id}`) ?? {}) as Config, +const loadedPluginMap: Record> = {}; + +const createContext = (id: string, win: BrowserWindow): BackendContext => ({ + getConfig: () => + deepmerge( + mainPlugins[id].config, + config.get(`plugins.${id}`) ?? { enabled: false }, + ) as PluginConfig, setConfig: (newConfig) => { config.setPartial(`plugins.${id}`, newConfig); }, - send: (event: string, ...args: unknown[]) => { - win.webContents.send(event, ...args); - }, - handle: (event: string, listener) => { - ipcMain.handle(event, async (_, ...args) => listener(...args as never)); - }, - on: (event: string, listener) => { - ipcMain.on(event, async (_, ...args) => listener(...args as never)); + ipc: { + send: (event: string, ...args: unknown[]) => { + win.webContents.send(event, ...args); + }, + handle: (event: string, listener: CallableFunction) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + ipcMain.handle(event, (_, ...args: unknown[]) => listener(...args)); + }, + on: (event: string, listener: CallableFunction) => { + ipcMain.on(event, (_, ...args: unknown[]) => { + listener(...args); + }); + }, + removeHandler: (event: string) => { + ipcMain.removeHandler(event); + } }, + + window: win, }); -export const forceUnloadMainPlugin = (id: keyof PluginBuilderList, win: BrowserWindow) => { - unregisterStyleMap[id]?.forEach((unregister) => unregister()); - delete unregisterStyleMap[id]; +export const forceUnloadMainPlugin = async ( + id: string, + win: BrowserWindow, +): Promise => { + const plugin = loadedPluginMap[id]; + if (!plugin) return; - loadedPluginMap[id]?.onUnload?.(win); - delete loadedPluginMap[id]; + return new Promise((resolve, reject) => { + try { + const hasStopped = stopPlugin(id, plugin, { + ctx: 'backend', + context: createContext(id, win), + }); + if (!hasStopped) { + console.log( + '[YTMusic]', + `Cannot unload "${id}" plugin: no stop function`, + ); + reject(); + return; + } - console.log('[YTMusic]', `"${id}" plugin is unloaded`); + delete loadedPluginMap[id]; + console.log('[YTMusic]', `"${id}" plugin is unloaded`); + resolve(); + } catch (err) { + console.log('[YTMusic]', `Cannot unload "${id}" plugin: ${String(err)}`); + reject(err); + } + }); }; -export const forceLoadMainPlugin = async (id: keyof PluginBuilderList, win: BrowserWindow) => { - const builder = allPluginBuilders[id]; +export const forceLoadMainPlugin = async ( + id: string, + win: BrowserWindow, +): Promise => { + const plugin = mainPlugins[id]; + if (!plugin) return; - Promise.allSettled( - builder.styles?.map(async (style) => { - const unregister = await injectCSS(win.webContents, style); - console.log('[YTMusic]', `Injected CSS for "${id}" plugin`); - - return unregister; - }) ?? [], - ).then((result) => { - unregisterStyleMap[id] = result - .map((it) => it.status === 'fulfilled' && it.value) - .filter(Boolean); - - let isInjectSuccess = true; - result.forEach((it) => { - if (it.status === 'rejected') { - isInjectSuccess = false; - - console.log('[YTMusic]', `Cannot inject "${id}" plugin style: ${String(it.reason)}`); + return new Promise((resolve, reject) => { + try { + const hasStarted = startPlugin(id, plugin, { + ctx: 'backend', + context: createContext(id, win), + }); + if (!hasStarted) { + console.log('[YTMusic]', `Cannot load "${id}" plugin`); + reject(); + return; } - }); - if (isInjectSuccess) console.log('[YTMusic]', `"${id}" plugin data is loaded`); + + loadedPluginMap[id] = plugin; + resolve(); + } catch (err) { + console.error( + '[YTMusic]', + `Cannot initialize "${id}" plugin: `, + ); + console.trace(err); + reject(err); + } }); - - try { - const factory = allPluginFactoryList[id]; - if (!factory) return; - - const context = createContext(id, win); - const plugin = await factory(context); - loadedPluginMap[id] = plugin; - plugin.onLoad?.(win); - - console.log('[YTMusic]', `"${id}" plugin is loaded`); - } catch (err) { - console.log('[YTMusic]', `Cannot initialize "${id}" plugin: ${String(err)}`); - } }; export const loadAllMainPlugins = async (win: BrowserWindow) => { + console.log('[YTMusic]', 'Loading all plugins'); const pluginConfigs = config.plugins.getPlugins(); + const queue: Promise[] = []; - for (const [pluginId, builder] of Object.entries(allPluginBuilders)) { - const typedBuilder = builder as PluginBuilderList[keyof PluginBuilderList]; - - const config = deepmerge(typedBuilder.config, pluginConfigs[pluginId as keyof PluginBuilderList] ?? {}); - + for (const [plugin, pluginDef] of Object.entries(mainPlugins)) { + const config = deepmerge(pluginDef.config, pluginConfigs[plugin] ?? {}); if (config.enabled) { - await forceLoadMainPlugin(pluginId as keyof PluginBuilderList, win); - } else { - if (loadedPluginMap[pluginId as keyof PluginBuilderList]) { - forceUnloadMainPlugin(pluginId as keyof PluginBuilderList, win); - } + queue.push(forceLoadMainPlugin(plugin, win)); + } else if (loadedPluginMap[plugin]) { + queue.push(forceUnloadMainPlugin(plugin, win)); } } + + await Promise.allSettled(queue); }; export const unloadAllMainPlugins = (win: BrowserWindow) => { for (const id of Object.keys(loadedPluginMap)) { - forceUnloadMainPlugin(id as keyof PluginBuilderList, win); + forceUnloadMainPlugin(id, win); } }; -export const getLoadedMainPlugin = (id: Key): MainPlugin | undefined => { +export const getLoadedMainPlugin = (id: string): PluginDef | undefined => { return loadedPluginMap[id]; }; + export const getAllLoadedMainPlugins = () => { return loadedPluginMap; }; -export const registerMainPlugin = ( - id: string, - builder: PluginBuilder, - factory?: MainPluginFactory, -) => { - if (factory) allPluginFactoryList[id] = factory; - allPluginBuilders[id] = builder; -}; diff --git a/src/loader/menu.ts b/src/loader/menu.ts index 901cfc85..210107f1 100644 --- a/src/loader/menu.ts +++ b/src/loader/menu.ts @@ -1,26 +1,22 @@ import { deepmerge } from 'deepmerge-ts'; +import { allPlugins } from 'virtual:plugins'; -import { MenuPluginContext, MenuPluginFactory, PluginBaseConfig, PluginBuilder } from '../plugins/utils/builder'; -import config from '../config'; -import { setApplicationMenu } from '../menu'; +import config from '@/config'; +import { setApplicationMenu } from '@/menu'; +import type { MenuContext } from '@/types/contexts'; import type { BrowserWindow, MenuItemConstructorOptions } from 'electron'; +import type { PluginConfig } from '@/types/plugins'; -const allPluginFactoryList: Record> = {}; -const allPluginBuilders: Record> = {}; const menuTemplateMap: Record = {}; - -const createContext = < - Key extends keyof PluginBuilderList, - Config extends PluginBaseConfig = PluginBuilderList[Key]['config'], ->(id: Key, win: BrowserWindow): MenuPluginContext => ({ - getConfig: () => deepmerge(allPluginBuilders[id].config, config.get(`plugins.${id}`) ?? {}) as Config, +const createContext = (id: string, win: BrowserWindow): MenuContext => ({ + getConfig: () => config.plugins.getOptions(id), setConfig: (newConfig) => { config.setPartial(`plugins.${id}`, newConfig); }, window: win, - refresh: async () => { - await setApplicationMenu(win); + refresh: () => { + setApplicationMenu(win); if (config.plugins.isEnabled('in-app-menu')) { win.webContents.send('refresh-in-app-menu'); @@ -28,45 +24,40 @@ const createContext = < }, }); -export const forceLoadMenuPlugin = async (id: keyof PluginBuilderList, win: BrowserWindow) => { +export const forceLoadMenuPlugin = async (id: string, win: BrowserWindow) => { try { - const factory = allPluginFactoryList[id]; - if (!factory) return; + const plugin = allPlugins[id]; + if (!plugin) return; - const context = createContext(id, win); - menuTemplateMap[id] = await factory(context); + const menu = plugin.menu?.(createContext(id, win)); + if (menu) menuTemplateMap[id] = await menu; + else return; - console.log('[YTMusic]', `"${id}" plugin is loaded`); + console.log('[YTMusic]', `Successfully loaded '${id}::menu'`); } catch (err) { - console.log('[YTMusic]', `Cannot initialize "${id}" plugin: ${String(err)}`); + console.error('[YTMusic]', `Cannot initialize '${id}::menu': `); + console.trace(err); } }; -export const loadAllMenuPlugins = async (win: BrowserWindow) => { +export const loadAllMenuPlugins = (win: BrowserWindow) => { const pluginConfigs = config.plugins.getPlugins(); - for (const [pluginId, builder] of Object.entries(allPluginBuilders)) { - const typedBuilder = builder as PluginBuilderList[keyof PluginBuilderList]; - - const config = deepmerge(typedBuilder.config, pluginConfigs[pluginId as keyof PluginBuilderList] ?? {}); + for (const [pluginId, pluginDef] of Object.entries(allPlugins)) { + const config = deepmerge(pluginDef.config, pluginConfigs[pluginId] ?? {}); if (config.enabled) { - await forceLoadMenuPlugin(pluginId as keyof PluginBuilderList, win); + forceLoadMenuPlugin(pluginId, win); } } }; -export const getMenuTemplate = (id: Key): MenuItemConstructorOptions[] | undefined => { +export const getMenuTemplate = ( + id: string, +): MenuItemConstructorOptions[] | undefined => { return menuTemplateMap[id]; }; + export const getAllMenuTemplate = () => { return menuTemplateMap; }; -export const registerMenuPlugin = ( - id: string, - builder: PluginBuilder, - factory?: MenuPluginFactory, -) => { - if (factory) allPluginFactoryList[id] = factory; - allPluginBuilders[id] = builder; -}; diff --git a/src/loader/preload.ts b/src/loader/preload.ts index dc598a1e..68297d0e 100644 --- a/src/loader/preload.ts +++ b/src/loader/preload.ts @@ -1,68 +1,66 @@ import { deepmerge } from 'deepmerge-ts'; +import { preloadPlugins } from 'virtual:plugins'; -import { - PluginBaseConfig, - PluginBuilder, - PreloadPlugin, - PluginContext, - PreloadPluginFactory -} from '../plugins/utils/builder'; -import config from '../config'; +import { startPlugin, stopPlugin } from '@/utils'; -const allPluginFactoryList: Record> = {}; -const allPluginBuilders: Record> = {}; -const unregisterStyleMap: Record void)[]> = {}; -const loadedPluginMap: Record> = {}; +import config from '@/config'; -const createContext = < - Key extends keyof PluginBuilderList, - Config extends PluginBaseConfig = PluginBuilderList[Key]['config'], ->(id: Key): PluginContext => ({ - getConfig: () => deepmerge(allPluginBuilders[id].config, config.get(`plugins.${id}`) ?? {}) as Config, +import type { PreloadContext } from '@/types/contexts'; +import type { PluginConfig, PluginDef } from '@/types/plugins'; + +const loadedPluginMap: Record> = {}; +const createContext = (id: string): PreloadContext => ({ + getConfig: () => config.plugins.getOptions(id), setConfig: (newConfig) => { config.setPartial(`plugins.${id}`, newConfig); }, }); -export const forceUnloadPreloadPlugin = (id: keyof PluginBuilderList) => { - unregisterStyleMap[id]?.forEach((unregister) => unregister()); - delete unregisterStyleMap[id]; - - loadedPluginMap[id]?.onUnload?.(); - delete loadedPluginMap[id]; - +export const forceUnloadPreloadPlugin = (id: string) => { + const hasStopped = stopPlugin(id, loadedPluginMap[id], { + ctx: 'preload', + context: createContext(id), + }); + if (!hasStopped) { + console.log('[YTMusic]', `Cannot stop "${id}" plugin`); + return; + } console.log('[YTMusic]', `"${id}" plugin is unloaded`); }; -export const forceLoadPreloadPlugin = async (id: keyof PluginBuilderList) => { +export const forceLoadPreloadPlugin = (id: string) => { try { - const factory = allPluginFactoryList[id]; - if (!factory) return; + const plugin = preloadPlugins[id]; + if (!plugin) return; - const context = createContext(id); - const plugin = await factory(context); - loadedPluginMap[id] = plugin; - plugin.onLoad?.(); + const hasStarted = startPlugin(id, plugin, { + ctx: 'preload', + context: createContext(id), + }); + + if (hasStarted) loadedPluginMap[id] = plugin; console.log('[YTMusic]', `"${id}" plugin is loaded`); } catch (err) { - console.log('[YTMusic]', `Cannot initialize "${id}" plugin: ${String(err)}`); + console.error( + '[YTMusic]', + `Cannot initialize "${id}" plugin: `, + ); + console.trace(err); } }; -export const loadAllPreloadPlugins = async () => { +export const loadAllPreloadPlugins = () => { const pluginConfigs = config.plugins.getPlugins(); - for (const [pluginId, builder] of Object.entries(allPluginBuilders)) { - const typedBuilder = builder as PluginBuilderList[keyof PluginBuilderList]; - - const config = deepmerge(typedBuilder.config, pluginConfigs[pluginId as keyof PluginBuilderList] ?? {}); + for (const [pluginId, pluginDef] of Object.entries(preloadPlugins)) { + const config = deepmerge(pluginDef.config, pluginConfigs[pluginId] ?? {}) ; if (config.enabled) { - await forceLoadPreloadPlugin(pluginId as keyof PluginBuilderList); + forceLoadPreloadPlugin(pluginId); } else { - if (loadedPluginMap[pluginId as keyof PluginBuilderList]) { - forceUnloadPreloadPlugin(pluginId as keyof PluginBuilderList); + if (loadedPluginMap[pluginId]) { + forceUnloadPreloadPlugin(pluginId); } } } @@ -70,21 +68,14 @@ export const loadAllPreloadPlugins = async () => { export const unloadAllPreloadPlugins = () => { for (const id of Object.keys(loadedPluginMap)) { - forceUnloadPreloadPlugin(id as keyof PluginBuilderList); + forceUnloadPreloadPlugin(id); } }; -export const getLoadedPreloadPlugin = (id: Key): PreloadPlugin | undefined => { +export const getLoadedPreloadPlugin = (id: string): PluginDef | undefined => { return loadedPluginMap[id]; }; + export const getAllLoadedPreloadPlugins = () => { return loadedPluginMap; }; -export const registerPreloadPlugin = ( - id: string, - builder: PluginBuilder, - factory?: PreloadPluginFactory, -) => { - if (factory) allPluginFactoryList[id] = factory; - allPluginBuilders[id] = builder; -}; diff --git a/src/loader/renderer.ts b/src/loader/renderer.ts index d19b3a88..637a2ced 100644 --- a/src/loader/renderer.ts +++ b/src/loader/renderer.ts @@ -1,75 +1,92 @@ import { deepmerge } from 'deepmerge-ts'; -import { - PluginBaseConfig, PluginBuilder, - RendererPlugin, - RendererPluginContext, - RendererPluginFactory -} from '../plugins/utils/builder'; +import { rendererPlugins } from 'virtual:plugins'; + +import { startPlugin, stopPlugin } from '@/utils'; + +import type { RendererContext } from '@/types/contexts'; +import type { PluginConfig, PluginDef } from '@/types/plugins'; -const allPluginFactoryList: Record> = {}; -const allPluginBuilders: Record> = {}; const unregisterStyleMap: Record void)[]> = {}; -const loadedPluginMap: Record> = {}; +const loadedPluginMap: Record> = {}; -const createContext = < - Key extends keyof PluginBuilderList, - Config extends PluginBaseConfig = PluginBuilderList[Key]['config'], ->(id: Key): RendererPluginContext => ({ - getConfig: async () => { - return await window.ipcRenderer.invoke('get-config', id) as Config; - }, +export const createContext = (id: string): RendererContext => ({ + getConfig: async () => window.ipcRenderer.invoke('get-config', id), setConfig: async (newConfig) => { await window.ipcRenderer.invoke('set-config', id, newConfig); }, - - invoke: async (event: string, ...args: unknown[]): Promise => { - return await window.ipcRenderer.invoke(event, ...args) as Return; - }, - on: (event: string, listener) => { - window.ipcRenderer.on(event, async (_, ...args) => listener(...args as never)); + ipc: { + send: (event: string, ...args: unknown[]) => { + window.ipcRenderer.send(event, ...args); + }, + invoke: (event: string, ...args: unknown[]) => window.ipcRenderer.invoke(event, ...args), + on: (event: string, listener: CallableFunction) => { + window.ipcRenderer.on(event, (_, ...args: unknown[]) => { + listener(...args); + }); + }, + removeAllListeners: (event: string) => { + window.ipcRenderer.removeAllListeners(event); + } }, }); -export const forceUnloadRendererPlugin = (id: keyof PluginBuilderList) => { +export const forceUnloadRendererPlugin = (id: string) => { unregisterStyleMap[id]?.forEach((unregister) => unregister()); - delete unregisterStyleMap[id]; - loadedPluginMap[id]?.onUnload?.(); + delete unregisterStyleMap[id]; delete loadedPluginMap[id]; + const plugin = rendererPlugins[id]; + if (!plugin) return; + + stopPlugin(id, plugin, { ctx: 'renderer', context: createContext(id) }); + if (plugin?.stylesheets) + document.querySelector(`style#plugin-${id}`)?.remove(); + console.log('[YTMusic]', `"${id}" plugin is unloaded`); }; -export const forceLoadRendererPlugin = async (id: keyof PluginBuilderList) => { - try { - const factory = allPluginFactoryList[id]; - if (!factory) return; +export const forceLoadRendererPlugin = (id: string) => { + const plugin = rendererPlugins[id]; + if (!plugin) return; - const context = createContext(id); - const plugin = await factory(context); + const hasEvaled = startPlugin(id, plugin, { + ctx: 'renderer', + context: createContext(id), + }); + + if (hasEvaled || plugin?.stylesheets) { loadedPluginMap[id] = plugin; - plugin.onLoad?.(); - console.log('[YTMusic]', `"${id}" plugin is loaded`); - } catch (err) { - console.log('[YTMusic]', `Cannot initialize "${id}" plugin: ${String(err)}`); + if (plugin?.stylesheets) { + const styleSheetList = plugin.stylesheets.map((style) => { + const styleSheet = new CSSStyleSheet(); + styleSheet.replaceSync(style); + + return styleSheet; + }); + + document.adoptedStyleSheets = [...document.adoptedStyleSheets, ...styleSheetList]; + } + + if (!hasEvaled) console.log('[YTMusic]', `"${id}" plugin is loaded`); + } else { + console.log('[YTMusic]', `Cannot initialize "${id}" plugin`); } }; -export const loadAllRendererPlugins = async () => { +export const loadAllRendererPlugins = () => { const pluginConfigs = window.mainConfig.plugins.getPlugins(); - for (const [pluginId, builder] of Object.entries(allPluginBuilders)) { - const typedBuilder = builder as PluginBuilderList[keyof PluginBuilderList]; - - const config = deepmerge(typedBuilder.config, pluginConfigs[pluginId as keyof PluginBuilderList] ?? {}); + for (const [pluginId, pluginDef] of Object.entries(rendererPlugins)) { + const config = deepmerge(pluginDef.config, pluginConfigs[pluginId] ?? {}) ; if (config.enabled) { - await forceLoadRendererPlugin(pluginId as keyof PluginBuilderList); + forceLoadRendererPlugin(pluginId); } else { - if (loadedPluginMap[pluginId as keyof PluginBuilderList]) { - forceUnloadRendererPlugin(pluginId as keyof PluginBuilderList); + if (loadedPluginMap[pluginId]) { + forceUnloadRendererPlugin(pluginId); } } } @@ -77,21 +94,14 @@ export const loadAllRendererPlugins = async () => { export const unloadAllRendererPlugins = () => { for (const id of Object.keys(loadedPluginMap)) { - forceUnloadRendererPlugin(id as keyof PluginBuilderList); + forceUnloadRendererPlugin(id); } }; -export const getLoadedRendererPlugin = (id: Key): RendererPlugin | undefined => { +export const getLoadedRendererPlugin = (id: string): PluginDef | undefined => { return loadedPluginMap[id]; }; + export const getAllLoadedRendererPlugins = () => { return loadedPluginMap; }; -export const registerRendererPlugin = ( - id: string, - builder: PluginBuilder, - factory?: RendererPluginFactory, -) => { - if (factory) allPluginFactoryList[id] = factory; - allPluginBuilders[id] = builder; -}; diff --git a/src/menu.ts b/src/menu.ts index d8ddb75e..26ad7192 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -1,31 +1,35 @@ import is from 'electron-is'; -import { app, BrowserWindow, clipboard, dialog, Menu } from 'electron'; +import { + app, + BrowserWindow, + clipboard, + dialog, + Menu, + MenuItem, +} from 'electron'; import prompt from 'custom-electron-prompt'; -import { restart } from './providers/app-controls'; +import { allPlugins } from 'virtual:plugins'; + import config from './config'; + +import { restart } from './providers/app-controls'; import { startingPages } from './providers/extracted-data'; import promptOptions from './providers/prompt-options'; -/* eslint-disable import/order */ -import { menuPlugins as menuList } from 'virtual:MenuPlugins'; -import { pluginBuilders } from 'virtual:PluginBuilders'; -/* eslint-enable import/order */ - -import { getAvailablePluginNames } from './plugins/utils/main'; -import { - MenuPluginFactory, - PluginBaseConfig, - PluginBuilder -} from './plugins/utils/builder'; -import { getAllMenuTemplate, loadAllMenuPlugins, registerMenuPlugin } from './loader/menu'; +import { getAllMenuTemplate, loadAllMenuPlugins } from './loader/menu'; export type MenuTemplate = Electron.MenuItemConstructorOptions[]; // True only if in-app-menu was loaded on launch const inAppMenuActive = config.plugins.isEnabled('in-app-menu'); -const pluginEnabledMenu = (plugin: string, label = '', hasSubmenu = false, refreshMenu: (() => void ) | undefined = undefined): Electron.MenuItemConstructorOptions => ({ +const pluginEnabledMenu = ( + plugin: string, + label = '', + hasSubmenu = false, + refreshMenu: (() => void) | undefined = undefined, +): Electron.MenuItemConstructorOptions => ({ label: label || plugin, type: 'checkbox', checked: config.plugins.isEnabled(plugin), @@ -49,50 +53,52 @@ export const refreshMenu = (win: BrowserWindow) => { } }; -Object.entries(pluginBuilders).forEach(([id, builder]) => { - const typedBuilder = builder as PluginBuilder; - const plugin = menuList[id] as MenuPluginFactory | undefined; - - registerMenuPlugin(id, typedBuilder, plugin); -}); - -export const mainMenuTemplate = async (win: BrowserWindow): Promise => { +export const mainMenuTemplate = (win: BrowserWindow): MenuTemplate => { const innerRefreshMenu = () => refreshMenu(win); - await loadAllMenuPlugins(win); + loadAllMenuPlugins(win); - const menuResult = Object.entries(getAllMenuTemplate()).map(([id, template]) => { - const pluginLabel = (pluginBuilders[id as keyof PluginBuilderList])?.name ?? id; + const menuResult = Object.entries(getAllMenuTemplate()).map( + ([id, template]) => { + const pluginLabel = allPlugins[id]?.name ?? id; + + if (!config.plugins.isEnabled(id)) { + return [ + id, + pluginEnabledMenu(id, pluginLabel, true, innerRefreshMenu), + ] as const; + } - if (!config.plugins.isEnabled(id)) { return [ id, - pluginEnabledMenu(id, pluginLabel, true, innerRefreshMenu), + { + label: pluginLabel, + submenu: [ + pluginEnabledMenu(id, 'Enabled', true, innerRefreshMenu), + { type: 'separator' }, + ...template, + ], + } satisfies Electron.MenuItemConstructorOptions, ] as const; - } + }, + ); - return [ - id, - { - label: pluginLabel, - submenu: [ - pluginEnabledMenu(id, 'Enabled', true, innerRefreshMenu), - { type: 'separator' }, - ...template, - ], - } satisfies Electron.MenuItemConstructorOptions - ] as const; - }); + const availablePlugins = Object.keys(allPlugins); + const pluginMenus = availablePlugins + .sort((a, b) => { + const aPluginLabel = allPlugins[a]?.name ?? a; + const bPluginLabel = allPlugins[b]?.name ?? b; - const availablePlugins = getAvailablePluginNames(); - const pluginMenus = availablePlugins.map((id) => { - const predefinedTemplate = menuResult.find((it) => it[0] === id); - if (predefinedTemplate) return predefinedTemplate[1]; + return aPluginLabel.localeCompare(bPluginLabel); + }) + .map((id) => { + const predefinedTemplate = menuResult.find((it) => it[0] === id); + if (predefinedTemplate) return predefinedTemplate[1]; - const pluginLabel = pluginBuilders[id as keyof PluginBuilderList]?.name ?? id; + const pluginLabel = allPlugins[id]?.name ?? id; - return pluginEnabledMenu(id, pluginLabel, true, innerRefreshMenu); - }); + return pluginEnabledMenu(id, pluginLabel, true, innerRefreshMenu); + }); return [ { @@ -106,7 +112,7 @@ export const mainMenuTemplate = async (win: BrowserWindow): Promise { - const subMenuArray: Electron.MenuItemConstructorOptions[] = Object.keys(startingPages).map((name) => ({ - label: name, - type: 'radio', - checked: config.get('options.startingPage') === name, - click() { - config.set('options.startingPage', name); - }, - })); + const subMenuArray: Electron.MenuItemConstructorOptions[] = + Object.keys(startingPages).map((name) => ({ + label: name, + type: 'radio', + checked: config.get('options.startingPage') === name, + click() { + config.set('options.startingPage', name); + }, + })); subMenuArray.unshift({ label: 'Unset', type: 'radio', @@ -147,8 +154,11 @@ export const mainMenuTemplate = async (win: BrowserWindow): Promise