diff --git a/electron.vite.config.ts b/electron.vite.config.ts index cc9da5f..e5ca0e6 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -17,7 +17,7 @@ const resolveOptions: UserConfigExport = { }, }, }; -const externalizedEsmDeps = ["lodash-es", "@faker-js/faker", "@trpc-limiter/memory", "got"]; +const externalizedEsmDeps = ["lodash-es", "@faker-js/faker", "@trpc-limiter/memory", "got", "encryption.js"]; export default defineConfig({ main: { ...resolveOptions, diff --git a/package.json b/package.json index 513fde1..ae61223 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ytmdesktop2", - "version": "0.14.0", + "version": "0.14.1", "private": false, "author": "Venipa ", "main": "./out/main/index.js", @@ -35,17 +35,18 @@ "chalk": "^5.0.1", "core-js": "^3.24.1", "cors": "^2.8.5", + "crypto-js": "^4.2.0", "daisyui": "^2.20.0", "date-fns": "^2.29.1", "dompurify": "^3.1.7", "electron-conf": "^1.2.1", "electron-threads": "^1.0.2", "electron-updater": "^6.3.9", + "encryption.js": "^1.0.6", "events": "^3.3.0", "express": "^4.18.1", "express-ws": "^5.0.2", "got": "^11.8.6", - "keytar": "^7.9.0", "lodash-es": "^4.17.21", "lucide-vue-next": "^0.454.0", "node-fetch": "^2.6.7", @@ -72,6 +73,7 @@ "@electron-toolkit/tsconfig": "^1.0.1", "@rushstack/eslint-patch": "^1.10.4", "@types/cors": "^2.8.17", + "@types/crypto-js": "^4.2.2", "@types/electron-devtools-installer": "^2.2.0", "@types/events": "^3.0.0", "@types/express": "^4.17.13", diff --git a/src/main/lib/secureStore/index.ts b/src/main/lib/secureStore/index.ts new file mode 100644 index 0000000..badebfc --- /dev/null +++ b/src/main/lib/secureStore/index.ts @@ -0,0 +1,54 @@ +import { createEncryptedStore } from "../store/createYmlStore"; +type Credential = { + account: string; + password: string; +}; +type Credentials = Array; +type CredentialStore = { + credentials: Record; +}; +const store = createEncryptedStore("credentials", { + defaults: { credentials: {} }, +}); +class SecureStore { + getAll() { + return new Promise((resolve, reject) => + resolve( + Object.entries(store.get("credentials", {})).map( + ([account, password]) => + ({ + account, + password, + }) as Credential, + ), + ), + ); + } + set(key: string, value: string) { + return new Promise(async (resolve, reject) => { + store.set({ + credentials: { + [key]: value, + }, + }); + return resolve(value); + }); + } + get(key: string) { + return new Promise(async (resolve, reject) => { + const value = store.get(`credentials.${key}`, null); + return resolve(value); + }); + } + delete(key: string) { + return new Promise(async (resolve, reject) => { + store.delete(`credentials.${key}`); + return resolve(true); + }); + } + readonly setPassword: typeof this.set = this.set.bind(this); + readonly getPassword: typeof this.get = this.get.bind(this); +} + +const secureStore = new SecureStore(); +export default secureStore; diff --git a/src/main/lib/store/createYmlStore.ts b/src/main/lib/store/createYmlStore.ts index a75fc2b..9bff0e2 100644 --- a/src/main/lib/store/createYmlStore.ts +++ b/src/main/lib/store/createYmlStore.ts @@ -1,7 +1,10 @@ -import { SlugifyOptions } from "@shared/slug"; +import slugify, { SlugifyOptions } from "@shared/slug"; +import { base64 } from "@shared/utils/base64"; +import { generateRandom } from "@shared/utils/randomString"; import { app } from "electron"; import { ConfOptions as Options, Conf as Store } from "electron-conf/main"; -import { mkdirSync, statSync } from "node:fs"; +import Encryption from "encryption.js"; +import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; import path from "node:path"; import { parse as deserialize, stringify as serialize } from "yaml"; const slugifyOptions = { @@ -10,8 +13,7 @@ const slugifyOptions = { trim: true, remove: /[*+~.()'"!:@]/g, } as SlugifyOptions; -const getStoreUserData = () => - import.meta.env.PROD ? app.getPath("userData") : path.join("out/store"); +const getStoreUserData = () => app.getPath("userData"); if (!statSync(getStoreUserData(), { throwIfNoEntry: false })) mkdirSync(getStoreUserData(), { recursive: true }); export const createYmlStore = = Record>( @@ -31,3 +33,30 @@ export const createYmlStore = = Record = Record>( + name: string, + options: Options = {} as Options, +) => { + const encryptionKeyPath = path.join(getStoreUserData(), slugify(name, slugifyOptions) + ".key"); + const enc = new Encryption({ secret: base64.encode(name) }); + if (!existsSync(encryptionKeyPath)) writeFileSync(encryptionKeyPath, enc.encrypt({ name, secret: generateRandom(32) })); + const encryptionKey = readFileSync(encryptionKeyPath).toString("utf8"); + const payload = enc.decrypt<{ name: string; secret: string }>(encryptionKey); + if (!payload || name !== payload?.name) throw new Error("Invalid encryption key"); + if (!payload.secret) throw new Error("Invalid encryption secret"); + const storeEncryptor = new Encryption({ secret: payload.secret }); + return new Store({ + ext: ".ytm", + ...options, + serializer: { + read(raw) { + return storeEncryptor.decrypt(raw); + }, + write(value) { + return storeEncryptor.encrypt(value); + }, + }, + name, + }); +}; diff --git a/src/main/plugins/lastfmProvider.plugin.ts b/src/main/plugins/lastfmProvider.plugin.ts index a027c9e..197004d 100644 --- a/src/main/plugins/lastfmProvider.plugin.ts +++ b/src/main/plugins/lastfmProvider.plugin.ts @@ -1,11 +1,11 @@ +import secureStore from "@main/lib/secureStore"; import { AfterInit, BaseProvider, OnInit } from "@main/utils/baseProvider"; import { IpcContext, IpcHandle } from "@main/utils/onIpcEvent"; import { string } from "@poppinss/utils/build/helpers"; import { App, BrowserWindow, shell } from "electron"; -import keytar from "keytar"; import { LastFMSettings } from "ytmd"; import { parseJson, stringifyJson } from "../lib/json"; -import { APP_KEYTAR, LASTFM_KEYTAR_SESSION, LASTFM_KEYTAR_TOKEN } from "../lib/keytar"; +import { LASTFM_KEYTAR_SESSION, LASTFM_KEYTAR_TOKEN } from "../lib/keytar"; import { LastFMClient } from "../lib/lastfm"; import IPC_EVENT_NAMES from "../utils/eventNames"; import { TrackData } from "../utils/trackData"; @@ -43,7 +43,7 @@ export default class LastFMProvider extends BaseProvider implements AfterInit, O } const lastfm = this.getProvider("settings").get("lastfm") as LastFMSettings; if (lastfm.enabled) { - const creds = await keytar.findCredentials(APP_KEYTAR); + const creds = await secureStore.getAll(); const lastFMState = creds.reduce( (acc, r) => { if (r.account === LASTFM_KEYTAR_TOKEN) acc.token = r.password; @@ -97,17 +97,17 @@ export default class LastFMProvider extends BaseProvider implements AfterInit, O win.webContents.on("did-navigate", async (ev, url, code, status) => { this.logger.debug(`[URL]> ${url}, ${code}, ${status}`); if (await hasSuccessInfo()) { - const userState = await win.webContents + const {userState}: LastFMUserState = await win.webContents .executeJavaScript(`document.getElementById("tlmdata")?.dataset?.tealiumData`) .then(parseJson) - .catch(() => null); + .catch(() => ({} as any)); this.logger.debug(`[Auth]> User: ${stringifyJson(userState)}`); - if (userState) { - await keytar.setPassword(APP_KEYTAR, LASTFM_KEYTAR_TOKEN, token); - const sessionToken = await this.client.getSession().catch(() => null); + if (userState === "authenticated") { + await secureStore.set(LASTFM_KEYTAR_TOKEN, token); + const sessionToken = await this.client.getSession() if (sessionToken) { - await keytar.setPassword(APP_KEYTAR, LASTFM_KEYTAR_SESSION, sessionToken); - if (win.isEnabled()) win.close(); + await secureStore.set(LASTFM_KEYTAR_SESSION, sessionToken); + if (!win.isDestroyed()) win.close(); } this.logger.debug(`[Auth]> Authenticated: ${sessionToken}`); @@ -173,8 +173,8 @@ export default class LastFMProvider extends BaseProvider implements AfterInit, O this.client.setAuthorize({ token: null, session: null }); settings.set("lastfm.name", null); await Promise.all([ - keytar.deletePassword(APP_KEYTAR, LASTFM_KEYTAR_SESSION).catch(() => null), - keytar.deletePassword(APP_KEYTAR, LASTFM_KEYTAR_TOKEN).catch(() => null), + secureStore.delete(LASTFM_KEYTAR_SESSION), + secureStore.delete(LASTFM_KEYTAR_TOKEN) ]); } this.sendState(); diff --git a/src/shared/utils/base64.ts b/src/shared/utils/base64.ts new file mode 100644 index 0000000..460c279 --- /dev/null +++ b/src/shared/utils/base64.ts @@ -0,0 +1,84 @@ +import { Buffer } from 'buffer'; + +/* + * @poppinss/utils + * + * (c) Poppinss + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Helper class to base64 encode/decode values with option + * for url encoding and decoding + */ +class Base64 { + /** + * Base64 encode Buffer or string + */ + encode(arrayBuffer: ArrayBuffer | SharedArrayBuffer): string; + encode(data: string, encoding?: BufferEncoding): string; + encode(data: ArrayBuffer | SharedArrayBuffer | string, encoding?: BufferEncoding): string { + if (typeof data === 'string') { + return Buffer.from(data, encoding).toString('base64'); + } + return Buffer.from(data).toString('base64'); + } + + /** + * Base64 decode a previously encoded string or Buffer. + */ + decode(encode: string, encoding: BufferEncoding, strict: true): string; + decode(encode: string, encoding: undefined, strict: true): string; + decode(encode: string, encoding?: BufferEncoding, strict?: false): string | null; + decode(encode: Buffer, encoding?: BufferEncoding): string; + decode(encoded: string | Buffer, encoding: BufferEncoding = 'utf8', strict: boolean = false): string | null { + if (Buffer.isBuffer(encoded)) { + return encoded.toString(encoding); + } + + const decoded = Buffer.from(encoded, 'base64').toString(encoding); + const isInvalid = this.encode(decoded, encoding) !== encoded; + + if (strict && isInvalid) { + throw new Error('Cannot decode malformed value'); + } + + return isInvalid ? null : decoded; + } + + /** + * Base64 encode Buffer or string to be URL safe. (RFC 4648) + */ + urlEncode(arrayBuffer: ArrayBuffer | SharedArrayBuffer): string; + urlEncode(data: string, encoding?: BufferEncoding): string; + urlEncode(data: ArrayBuffer | SharedArrayBuffer | string, encoding?: BufferEncoding): string { + const encoded = typeof data === 'string' ? this.encode(data, encoding) : this.encode(data); + return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, ''); + } + + /** + * Base64 URL decode a previously encoded string or Buffer. (RFC 4648) + */ + urlDecode(encode: string, encoding: BufferEncoding, strict: true): string; + urlDecode(encode: string, encoding: undefined, strict: true): string; + urlDecode(encode: string, encoding?: BufferEncoding, strict?: false): string | null; + urlDecode(encode: Buffer, encoding?: BufferEncoding): string; + urlDecode(encoded: string | Buffer, encoding: BufferEncoding = 'utf8', strict: boolean = false): string | null { + if (Buffer.isBuffer(encoded)) { + return encoded.toString(encoding); + } + + const decoded = Buffer.from(encoded, 'base64').toString(encoding); + const isInvalid = this.urlEncode(decoded, encoding) !== encoded; + + if (strict && isInvalid) { + throw new Error('Cannot urlDecode malformed value'); + } + + return isInvalid ? null : decoded; + } +} + +export const base64 = new Base64(); diff --git a/src/shared/utils/randomString.ts b/src/shared/utils/randomString.ts new file mode 100644 index 0000000..07901e6 --- /dev/null +++ b/src/shared/utils/randomString.ts @@ -0,0 +1,11 @@ +import { Buffer } from 'buffer'; +import { enc, lib } from 'crypto-js'; +import { base64 } from './base64'; +/** + * Generates a random string of a given size + */ +export function generateRandom(size: number): string { + const bits = (size + 1) * 6; + const buffer = Buffer.from(lib.WordArray.random(Math.ceil(bits / 8)).toString(enc.Hex), 'hex'); + return base64.urlEncode(buffer).slice(0, size); +} diff --git a/yarn.lock b/yarn.lock index 6a92f8a..96f75f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -396,9 +396,9 @@ optionalDependencies: global-agent "^3.0.0" -"@electron/node-gyp@git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2": +"@electron/node-gyp@https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2": version "10.2.0-electron.1" - resolved "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2" + resolved "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2" dependencies: env-paths "^2.2.0" exponential-backoff "^3.1.1" @@ -816,6 +816,11 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@lukeed/ms@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@lukeed/ms/-/ms-2.0.2.tgz#07f09e59a74c52f4d88c6db5c1054e819538e2a8" + integrity sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA== + "@malept/cross-spawn-promise@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz#d0772de1aa680a0bfb9ba2f32b4c828c7857cb9d" @@ -1552,6 +1557,11 @@ dependencies: "@types/node" "*" +"@types/crypto-js@^4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.2.2.tgz#771c4a768d94eb5922cc202a3009558204df0cea" + integrity sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ== + "@types/debug@^4.1.6": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" @@ -2665,7 +2675,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.0.3, bl@^4.1.0: +bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -3015,11 +3025,6 @@ change-case@^4.1.2: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - chownr@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" @@ -3300,6 +3305,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + css-select-base-adapter@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" @@ -3453,11 +3463,6 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3517,11 +3522,6 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-libc@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== - detect-libc@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" @@ -3838,7 +3838,17 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0, end-of-stream@^1.4.1: +encryption.js@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/encryption.js/-/encryption.js-1.0.6.tgz#cf2b5d38d3cdbd1668434392840ab1d93613919e" + integrity sha512-uQuFk8zuP4I37IP0f+xs9Ds7L3HiVNmlXx/ieTdhyozOLgZ6Lj5XF/Zmka/AeowkcuFLaYevnIb8BIIKWwIavg== + dependencies: + "@lukeed/ms" "^2.0.2" + crypto-js "^4.2.0" + safe-stable-stringify "^2.5.0" + secure-json-parse "^2.7.0" + +end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -4180,11 +4190,6 @@ exif-parser@^0.1.12: resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" integrity sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw== -expand-template@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" - integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== - exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" @@ -4453,11 +4458,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - fs-extra@^10.0.0, fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -4612,11 +4612,6 @@ gifwrap@^0.9.2: image-q "^4.0.0" omggif "^1.0.10" -github-from-package@0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" - integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== - glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -5016,11 +5011,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -5401,14 +5391,6 @@ jszip@^3.1.0: readable-stream "~2.3.6" setimmediate "^1.0.5" -keytar@^7.9.0: - version "7.9.0" - resolved "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb" - integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ== - dependencies: - node-addon-api "^4.3.0" - prebuild-install "^7.0.1" - keyv@^4.0.0: version "4.5.2" resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" @@ -5813,11 +5795,6 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== -minimist@^1.2.3: - version "1.2.7" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== - minimist@^1.2.5: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -5917,11 +5894,6 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: - version "0.5.3" - resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -5983,11 +5955,6 @@ nanoid@^3.3.7: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== -napi-build-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" - integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -6006,13 +5973,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-abi@^3.3.0: - version "3.31.0" - resolved "https://registry.npmjs.org/node-abi/-/node-abi-3.31.0.tgz#dfb2ea3d01188eb80859f69bb4a4354090c1b355" - integrity sha512-eSKV6s+APenqVh8ubJyiu/YhZgxQpGP66ntzUb3lY1xB9ukSRaGnx0AIxI+IM+1+IVYC1oWobgG5L3Lt9ARykQ== - dependencies: - semver "^7.3.5" - node-abi@^3.45.0: version "3.71.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.71.0.tgz#52d84bbcd8575efb71468fbaa1f9a49b2c242038" @@ -6025,11 +5985,6 @@ node-addon-api@^1.6.3: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== -node-addon-api@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== - node-api-version@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.2.0.tgz#5177441da2b1046a4d4547ab9e0972eed7b1ac1d" @@ -6698,24 +6653,6 @@ postgres-range@^1.1.1: resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.4.tgz#a59c5f9520909bcec5e63e8cf913a92e4c952863" integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== -prebuild-install@^7.0.1: - version "7.1.1" - resolved "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" - integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== - dependencies: - detect-libc "^2.0.0" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^1.0.1" - node-abi "^3.3.0" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^4.0.0" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -6839,16 +6776,6 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - read-binary-file-arch@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz#959c4637daa932280a9b911b1a6766a7e44288fc" @@ -6863,7 +6790,7 @@ read-cache@^1.0.0: dependencies: pify "^2.3.0" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -7079,7 +7006,7 @@ rxjs@^7.8.0: dependencies: tslib "^2.1.0" -safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -7094,6 +7021,11 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73" integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg== +safe-stable-stringify@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -7254,6 +7186,11 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +secure-json-parse@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -7391,20 +7328,6 @@ signal-exit@^4.0.1: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" - integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== - dependencies: - decompress-response "^6.0.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-html-tokenizer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/simple-html-tokenizer/-/simple-html-tokenizer-0.1.1.tgz#05c2eec579ffffe145a030ac26cfea61b980fabe" @@ -7649,11 +7572,6 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - sucrase@^3.32.0: version "3.35.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" @@ -7815,27 +7733,6 @@ tailwindcss@^3.4.14: resolve "^1.22.2" sucrase "^3.32.0" -tar-fs@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - tar@^6.0.5, tar@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" @@ -8021,13 +7918,6 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"