diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..5bb21de --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,45 @@ +name: Build & Verify + +on: + push: + branches: [ "main" ] # Runs on every push to main branch + +jobs: + build: + # Currently runs on macOS only, can add windows-latest later + runs-on: macos-latest + + steps: + - name: 🚚 Checkout code + uses: actions/checkout@v4 + + - name: βš™οΈ Setup Node.js environment + uses: actions/setup-node@v4 + with: + node-version: '20.x' # Node.js version compatible with project + cache: 'npm' # npm dependency caching for speed improvement + + - name: πŸ“¦ Install root dependencies + run: npm install + + - name: 🌐 Install and build web (Renderer) part + # Move to pickleglass_web directory and run commands + working-directory: ./pickleglass_web + run: | + npm install + npm run build + + - name: πŸ–₯️ Build Electron app + # Run Electron build script from root directory + run: npm run build + + - name: 🚨 Send failure notification to Slack + if: failure() + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_CHANNEL: general + SLACK_TITLE: "🚨 Build Failed" + SLACK_MESSAGE: "😭 Build failed for `${{ github.repository }}` repo on main branch." + SLACK_COLOR: 'danger' + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + diff --git a/.gitignore b/.gitignore index 09f6325..776dab2 100644 --- a/.gitignore +++ b/.gitignore @@ -102,7 +102,6 @@ pickleglass_web/venv/ node_modules/ npm-debug.log yarn-error.log -package-lock.json # Database data/*.db diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0c4ea82 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "aec"] + path = aec + url = https://github.com/samtiz/aec.git diff --git a/.npmrc b/.npmrc index d422cf4..5307c42 100644 --- a/.npmrc +++ b/.npmrc @@ -1,3 +1,2 @@ better-sqlite3:ignore-scripts=true -electron-deeplink:ignore-scripts=true sharp:ignore-scripts=true \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..26ad1a2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,95 @@ +# Contributing to Glass + +Thank you for considering contributing to **Glass by Pickle**! Contributions make the open-source community vibrant, innovative, and collaborative. We appreciate every contribution you makeβ€”big or small. + +## πŸ“Œ Contribution Guidelines + +### πŸ‘₯ Avoid Work Duplication + +Before creating an issue or submitting a pull request (PR), please check existing [Issues](https://github.com/pickle-com/glass/issues) and [Pull Requests](https://github.com/pickle-com/glass/pulls) to prevent duplicate efforts. + +### βœ… Start with Approved Issues + +- **Feature Requests**: Please wait for approval from core maintainers before starting work. Issues needing approval are marked with the `🚨 needs approval` label. +- **Bugs & Improvements**: You may begin immediately without explicit approval. + +### πŸ“ Clearly Document Your Work + +Provide enough context and detail to allow easy understanding. Issues and PRs should clearly communicate the problem or feature and stand alone without external references. + +### πŸ’‘ Summarize Pull Requests + +Include a brief summary at the top of your PR, describing the intent and scope of your changes. + +### πŸ”— Link Related Issues + +Use GitHub keywords (`Closes #123`, `Fixes #456`) to auto-link and close issues upon PR merge. + +### πŸ§ͺ Include Testing Information + +Clearly state how your changes were tested. + +> Example: +> "Tested locally on macOS 14, confirmed all features working as expected." + +### 🧠 Future-Proof Your Descriptions + +Document trade-offs, edge cases, and temporary workarounds clearly to help future maintainers understand your decisions. + +--- + +## πŸ”– Issue Priorities + +| Issue Type | Priority | +|----------------------------------------------------|---------------------| +| Minor enhancements & non-core feature requests | 🟒 Low Priority | +| UX improvements & minor bugs | 🟑 Medium Priority | +| Core functionalities & essential features | 🟠 High Priority | +| Critical bugs & breaking issues | πŸ”΄ Urgent | +| + + +# Developing + +### Prerequisites + +Ensure the following are installed: +- [Node.js v20.x.x](https://nodejs.org/en/download) +- [Python](https://www.python.org/downloads/) +- (Windows users) [Build Tools for Visual Studio](https://visualstudio.microsoft.com/downloads/) + +Ensure you're using Node.js version 20.x.x to avoid build errors with native dependencies. + +```bash +# Check your Node.js version +node --version + +# If you need to install Node.js 20.x.x, we recommend using nvm: +# curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash +# nvm install 20 +# nvm use 20 +``` + +## Setup and Build + +```bash +npm run setup +``` +Please ensure that you can make a full production build before pushing code. + + + +## Linting + +```bash +npm run lint +``` + +If you get errors, be sure to fix them before committing. + + +## Making a Pull Request + +- Be sure to [check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) when creating your PR. (This option isn't available if you're [contributing from a fork belonging to an organization](https://github.com/orgs/community/discussions/5634)) +- If your PR refers to or fixes an issue, add `refs #XXX` or `fixes #XXX` to the PR description. Replace `XXX` with the respective issue number. See more about [linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). +- Lastly, make sure to keep your branches updated (e.g., click the `Update branch` button on the GitHub PR page). \ No newline at end of file diff --git a/README.md b/README.md index a05a8bc..e5510e2 100644 --- a/README.md +++ b/README.md @@ -62,11 +62,14 @@ npm run setup booking-screen -### Use your own OpenAI API key, or sign up to use ours (free) +### Use your own API key, or sign up to use ours (free) booking-screen -You can visit [here](https://platform.openai.com/api-keys) to get your OpenAI API Key. +**Currently Supporting:** +- OpenAI API: Get OpenAI API Key [here](https://platform.openai.com/api-keys) +- Gemini API: Get Gemini API Key [here](https://aistudio.google.com/apikey) +- Local LLM (WIP) ### Liquid Glass Design (coming soon) @@ -88,20 +91,40 @@ You can visit [here](https://platform.openai.com/api-keys) to get your OpenAI AP `Ctrl/Cmd + Arrows` : move main window position +## Repo Activity + +![Alt](https://repobeats.axiom.co/api/embed/a23e342faafa84fa8797fa57762885d82fac1180.svg "Repobeats analytics image") + ## Contributing -We love contributions! Feel free to open issues for bugs or feature requests. +We love contributions! Feel free to open issues for bugs or feature requests. For detailed guide, please see our [contributing guide](/CONTRIBUTING.md). +> Currently, we're working on a full code refactor and modularization. Once that's completed, we'll jump into addressing the major issues. -## πŸ›  Current Issues & Improvements +### Contributors + + + + + +### Help Wanted Issues + +We have a list of [help wanted](https://github.com/pickle-com/glass/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22%F0%9F%99%8B%E2%80%8D%E2%99%82%EF%B8%8Fhelp%20wanted%22) that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process. + + +### πŸ›  Current Issues & Improvements | Status | Issue | Description | |--------|--------------------------------|---------------------------------------------------| -| 🚧 WIP | AEC Improvement | Transcription is not working occasionally | -| 🚧 WIP | Code Refactoring | Refactoring the entire codebase for better maintainability. | +| 🚧 WIP | Local LLM Support | Supporting Local LLM to power AI answers | | 🚧 WIP | Firebase Data Storage Issue | Session & ask should be saved in firebase for signup users | -| 🚧 WIP | Login Issue | Currently breaking when switching between local and sign-in mode | | 🚧 WIP | Liquid Glass | Liquid Glass UI for MacOS 26 | -| 🚧 WIP | Permission Issue | Mic & system audio & display capture permission sometimes not working| + +### Changelog + +- Jul 5: Now support Gemini, Intel Mac supported +- Jul 6: Full code refactoring has done. +- Jul 7: Now support Claude, LLM/STT model selection +- Jul 8: Now support Windows(beta), Improved AEC by Rust(to seperate mic/system audio), shortcut editing(beta) @@ -110,4 +133,4 @@ We love contributions! Feel free to open issues for bugs or feature requests. **Our mission is to build a living digital clone for everyone.** Glass is part of Step 1β€”a trusted pipeline that transforms your daily data into a scalable clone. Visit [pickle.com](https://pickle.com) to learn more. ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=pickle-com/glass&type=Date)](https://www.star-history.com/#pickle-com/glass&Date) \ No newline at end of file +[![Star History Chart](https://api.star-history.com/svg?repos=pickle-com/glass&type=Date)](https://www.star-history.com/#pickle-com/glass&Date) diff --git a/aec b/aec new file mode 160000 index 0000000..f00bb1f --- /dev/null +++ b/aec @@ -0,0 +1 @@ +Subproject commit f00bb1fb948053c752b916adfee19f90644a0b2f diff --git a/electron-builder.yml b/electron-builder.yml index 8f7d820..79b81fb 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -13,6 +13,12 @@ publish: repo: glass releaseType: draft +# Protocols configuration for deep linking +protocols: + name: PickleGlass Protocol + schemes: + - pickleglass + # List of files to be included in the app package files: - src/**/* @@ -29,6 +35,28 @@ extraResources: asarUnpack: - "src/assets/SystemAudioDump" +# Windows configuration +win: + icon: src/assets/logo.ico + target: + - target: nsis + arch: x64 + - target: portable + arch: x64 + requestedExecutionLevel: asInvoker + # Disable code signing to avoid symbolic link issues on Windows + signAndEditExecutable: false + +# NSIS installer configuration for Windows +nsis: + oneClick: false + perMachine: false + allowToChangeInstallationDirectory: true + deleteAppDataOnUninstall: true + createDesktopShortcut: always + createStartMenuShortcut: true + shortcutName: Glass + # macOS specific configuration mac: # The application category type diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..944e413 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12105 @@ +{ + "name": "pickle-glass", + "version": "0.2.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pickle-glass", + "version": "0.2.2", + "hasInstallScript": true, + "license": "GPL-3.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.56.0", + "@google/genai": "^1.8.0", + "@google/generative-ai": "^0.24.1", + "axios": "^1.10.0", + "better-sqlite3": "^9.4.3", + "cors": "^2.8.5", + "dotenv": "^17.0.0", + "electron-squirrel-startup": "^1.0.1", + "electron-store": "^8.2.0", + "electron-updater": "^6.6.2", + "express": "^4.18.2", + "firebase": "^11.10.0", + "firebase-admin": "^13.4.0", + "jsonwebtoken": "^9.0.2", + "node-fetch": "^2.7.0", + "openai": "^4.70.0", + "react-hot-toast": "^2.5.2", + "sharp": "^0.34.2", + "validator": "^13.11.0", + "wait-on": "^8.0.3", + "ws": "^8.18.0" + }, + "devDependencies": { + "@electron-forge/cli": "^7.8.1", + "@electron-forge/maker-deb": "^7.8.1", + "@electron-forge/maker-dmg": "^7.8.1", + "@electron-forge/maker-rpm": "^7.8.1", + "@electron-forge/maker-squirrel": "^7.8.1", + "@electron-forge/maker-zip": "^7.8.1", + "@electron-forge/plugin-auto-unpack-natives": "^7.8.1", + "@electron-forge/plugin-fuses": "^7.8.1", + "@electron/fuses": "^1.8.0", + "@electron/notarize": "^2.5.0", + "electron": "^30.5.1", + "electron-builder": "^26.0.12", + "electron-reloader": "^1.2.3", + "esbuild": "^0.25.5" + }, + "optionalDependencies": { + "electron-liquid-glass": "^1.0.1" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.56.0.tgz", + "integrity": "sha512-SLCB8M8+VMg1cpCucnA1XWHGWqVSZtIWzmOdDOEu3eTFZMB+A0sGZ1ESO5MHDnqrNTXz3safMrWx9x4rMZSOqA==", + "license": "MIT", + "bin": { + "anthropic-ai-sdk": "bin/cli" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@electron-forge/cli": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/cli/-/cli-7.8.1.tgz", + "integrity": "sha512-QI3EShutfq9Y+2TWWrPjm4JZM3eSAKzoQvRZdVhAfVpUbyJ8K23VqJShg3kGKlPf9BXHAGvE+8LyH5s2yDr1qA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.electron-forge-cli?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "MIT", + "dependencies": { + "@electron-forge/core": "7.8.1", + "@electron-forge/core-utils": "7.8.1", + "@electron-forge/shared-types": "7.8.1", + "@electron/get": "^3.0.0", + "chalk": "^4.0.0", + "commander": "^11.1.0", + "debug": "^4.3.1", + "fs-extra": "^10.0.0", + "listr2": "^7.0.2", + "log-symbols": "^4.0.0", + "semver": "^7.2.1" + }, + "bin": { + "electron-forge": "dist/electron-forge.js", + "electron-forge-vscode-nix": "script/vscode.sh", + "electron-forge-vscode-win": "script/vscode.cmd" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/core": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/core/-/core-7.8.1.tgz", + "integrity": "sha512-jkh0QPW5p0zmruu1E8+2XNufc4UMxy13WLJcm7hn9jbaXKLkMbKuEvhrN1tH/9uGp1mhr/t8sC4N67gP+gS87w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.electron-forge-core?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "MIT", + "dependencies": { + "@electron-forge/core-utils": "7.8.1", + "@electron-forge/maker-base": "7.8.1", + "@electron-forge/plugin-base": "7.8.1", + "@electron-forge/publisher-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1", + "@electron-forge/template-base": "7.8.1", + "@electron-forge/template-vite": "7.8.1", + "@electron-forge/template-vite-typescript": "7.8.1", + "@electron-forge/template-webpack": "7.8.1", + "@electron-forge/template-webpack-typescript": "7.8.1", + "@electron-forge/tracer": "7.8.1", + "@electron/get": "^3.0.0", + "@electron/packager": "^18.3.5", + "@electron/rebuild": "^3.7.0", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.3.1", + "fast-glob": "^3.2.7", + "filenamify": "^4.1.0", + "find-up": "^5.0.0", + "fs-extra": "^10.0.0", + "global-dirs": "^3.0.0", + "got": "^11.8.5", + "interpret": "^3.1.1", + "jiti": "^2.4.2", + "listr2": "^7.0.2", + "lodash": "^4.17.20", + "log-symbols": "^4.0.0", + "node-fetch": "^2.6.7", + "rechoir": "^0.8.0", + "semver": "^7.2.1", + "source-map-support": "^0.5.13", + "sudo-prompt": "^9.1.1", + "username": "^5.1.0" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/core-utils": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/core-utils/-/core-utils-7.8.1.tgz", + "integrity": "sha512-mRoPLDNZgmjyOURE/K0D3Op53XGFmFRgfIvFC7c9S/BqsRpovVblrqI4XxPRdNmH9dvhd8On9gGz+XIYAKD3aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1", + "@electron/rebuild": "^3.7.0", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.3.1", + "find-up": "^5.0.0", + "fs-extra": "^10.0.0", + "log-symbols": "^4.0.0", + "semver": "^7.2.1" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/maker-base": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-base/-/maker-base-7.8.1.tgz", + "integrity": "sha512-GUZqschGuEBzSzE0bMeDip65IDds48DZXzldlRwQ+85SYVA6RMU2AwDDqx3YiYsvP2OuxKruuqIJZtOF5ps4FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1", + "fs-extra": "^10.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/maker-deb": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-deb/-/maker-deb-7.8.1.tgz", + "integrity": "sha512-tjjeesQtCP5Xht1X7gl4+K9bwoETPmQfBkOVAY/FZIxPj40uQh/hOUtLX2tYENNGNVZ1ryDYRs8TuPi+I41Vfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/maker-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1" + }, + "engines": { + "node": ">= 16.4.0" + }, + "optionalDependencies": { + "electron-installer-debian": "^3.2.0" + } + }, + "node_modules/@electron-forge/maker-dmg": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-dmg/-/maker-dmg-7.8.1.tgz", + "integrity": "sha512-l449QvY2Teu+J9rHnjkTHEm/wOJ1LRfmrQ2QkGtFoTRcqvFWdUAEN8nK2/08w3j2h6tvOY3QSUjRzXrhJZRNRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/maker-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">= 16.4.0" + }, + "optionalDependencies": { + "electron-installer-dmg": "^5.0.1" + } + }, + "node_modules/@electron-forge/maker-rpm": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-rpm/-/maker-rpm-7.8.1.tgz", + "integrity": "sha512-TF6wylft3BHkw9zdHcxmjEPBZYgTIc0jE31skFnMEQ/aExbNRiNaCZvsXy+7ptTWZxhxUKRc9KHhLFRMCmOK8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/maker-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1" + }, + "engines": { + "node": ">= 16.4.0" + }, + "optionalDependencies": { + "electron-installer-redhat": "^3.2.0" + } + }, + "node_modules/@electron-forge/maker-squirrel": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-squirrel/-/maker-squirrel-7.8.1.tgz", + "integrity": "sha512-qT1PMvT7ALF0ONOkxlA0oc0PiFuKCAKgoMPoxYo9gGOqFvnAb+TBcnLxflQ4ashE/ZkrHpykr4LcDJxqythQTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/maker-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">= 16.4.0" + }, + "optionalDependencies": { + "electron-winstaller": "^5.3.0" + } + }, + "node_modules/@electron-forge/maker-zip": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/maker-zip/-/maker-zip-7.8.1.tgz", + "integrity": "sha512-unIxEoV1lnK4BLVqCy3L2y897fTyg8nKY1WT4rrpv0MUKnQG4qmigDfST5zZNNHHaulEn/ElAic2GEiP7d6bhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/maker-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1", + "cross-zip": "^4.0.0", + "fs-extra": "^10.0.0", + "got": "^11.8.5" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/plugin-auto-unpack-natives": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.8.1.tgz", + "integrity": "sha512-4URAgWX9qqqKe6Bfad0VmpFRrwINYMODfKGd2nFQrfHxmBtdpXnsWlLwVGE/wGssIQaTMI5bWQ6F2RNeXTgnhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/plugin-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/plugin-base": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/plugin-base/-/plugin-base-7.8.1.tgz", + "integrity": "sha512-iCZC2d7CbsZ9l6j5d+KPIiyQx0U1QBfWAbKnnQhWCSizjcrZ7A9V4sMFZeTO6+PVm48b/r9GFPm+slpgZtYQLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/plugin-fuses": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/plugin-fuses/-/plugin-fuses-7.8.1.tgz", + "integrity": "sha512-dYTwvbV1HcDOIQ0wTybpdtPq6YoBYXIWBTb7DJuvFu/c/thj1eoEdnbwr8mT9hEivjlu5p4ls46n16P5EtZ0oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/plugin-base": "7.8.1", + "@electron-forge/shared-types": "7.8.1" + }, + "engines": { + "node": ">= 16.4.0" + }, + "peerDependencies": { + "@electron/fuses": ">=1.0.0" + } + }, + "node_modules/@electron-forge/publisher-base": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/publisher-base/-/publisher-base-7.8.1.tgz", + "integrity": "sha512-z2C+C4pcFxyCXIFwXGDcxhU8qtVUPZa3sPL6tH5RuMxJi77768chLw2quDWk2/dfupcSELXcOMYCs7aLysCzeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/shared-types": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/shared-types/-/shared-types-7.8.1.tgz", + "integrity": "sha512-guLyGjIISKQQRWHX+ugmcjIOjn2q/BEzCo3ioJXFowxiFwmZw/oCZ2KlPig/t6dMqgUrHTH5W/F0WKu0EY4M+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/tracer": "7.8.1", + "@electron/packager": "^18.3.5", + "@electron/rebuild": "^3.7.0", + "listr2": "^7.0.2" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/template-base": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/template-base/-/template-base-7.8.1.tgz", + "integrity": "sha512-k8jEUr0zWFWb16ZGho+Es2OFeKkcbTgbC6mcH4eNyF/sumh/4XZMcwRtX1i7EiZAYiL9sVxyI6KVwGu254g+0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/core-utils": "7.8.1", + "@electron-forge/shared-types": "7.8.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "fs-extra": "^10.0.0", + "username": "^5.1.0" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/template-vite": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/template-vite/-/template-vite-7.8.1.tgz", + "integrity": "sha512-qzSlJaBYYqQAbBdLk4DqAE3HCNz4yXbpkb+VC74ddL4JGwPdPU57DjCthr6YetKJ2FsOVy9ipovA8HX5UbXpAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1", + "@electron-forge/template-base": "7.8.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/template-vite-typescript": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.8.1.tgz", + "integrity": "sha512-CccQhwUjZcc6svzuOi3BtbDal591DzyX2J5GPa6mwVutDP8EMtqJL1VyOHdcWO/7XjI6GNAD0fiXySOJiUAECA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1", + "@electron-forge/template-base": "7.8.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/template-webpack": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack/-/template-webpack-7.8.1.tgz", + "integrity": "sha512-DA77o9kTCHrq+W211pyNP49DyAt0d1mzMp2gisyNz7a+iKvlv2DsMAeRieLoCQ44akb/z8ZsL0YLteSjKLy4AA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1", + "@electron-forge/template-base": "7.8.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/template-webpack-typescript": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.8.1.tgz", + "integrity": "sha512-h922E+6zWwym1RT6WKD79BLTc4H8YxEMJ7wPWkBX59kw/exsTB/KFdiJq6r82ON5jSJ+Q8sDGqSmDWdyCfo+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-forge/shared-types": "7.8.1", + "@electron-forge/template-base": "7.8.1", + "fs-extra": "^10.0.0" + }, + "engines": { + "node": ">= 16.4.0" + } + }, + "node_modules/@electron-forge/tracer": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/@electron-forge/tracer/-/tracer-7.8.1.tgz", + "integrity": "sha512-r2i7aHVp2fylGQSPDw3aTcdNfVX9cpL1iL2MKHrCRNwgrfR+nryGYg434T745GGm1rNQIv5Egdkh5G9xf00oWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chrome-trace-event": "^1.0.3" + }, + "engines": { + "node": ">= 14.17.5" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" + } + }, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@electron/get/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@electron/get/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@electron/node-gyp": { + "version": "10.2.0-electron.1", + "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "integrity": "sha512-CrYo6TntjpoMO1SHjl5Pa/JoUsECNqNdB7Kx49WLQpWzPw53eEITJ2Hs9fh/ryUYDn4pxZz11StaBYBrLFJdqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^8.1.0", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.2.1", + "nopt": "^6.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron/node-gyp/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/node-gyp/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/node-gyp/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/packager": { + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@electron/packager/-/packager-18.3.6.tgz", + "integrity": "sha512-1eXHB5t+SQKvUiDpWGpvr90ZSSbXj+isrh3YbjCTjKT4bE4SQrKSBfukEAaBvp67+GXHFtCHjQgN9qSTFIge+Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@electron/asar": "^3.2.13", + "@electron/get": "^3.0.0", + "@electron/notarize": "^2.1.0", + "@electron/osx-sign": "^1.0.5", + "@electron/universal": "^2.0.1", + "@electron/windows-sign": "^1.0.0", + "debug": "^4.0.1", + "extract-zip": "^2.0.0", + "filenamify": "^4.1.0", + "fs-extra": "^11.1.0", + "galactus": "^1.0.0", + "get-package-info": "^1.0.0", + "junk": "^3.1.0", + "parse-author": "^2.0.0", + "plist": "^3.0.0", + "resedit": "^2.0.0", + "resolve": "^1.1.6", + "semver": "^7.1.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "electron-packager": "bin/electron-packager.js" + }, + "engines": { + "node": ">= 16.13.0" + }, + "funding": { + "url": "https://github.com/electron/packager?sponsor=1" + } + }, + "node_modules/@electron/packager/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/rebuild": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.2.tgz", + "integrity": "sha512-19/KbIR/DAxbsCkiaGMXIdPnMCJLkcf8AvGnduJtWBs/CBwiAjY1apCqOLVxrXg+rtXFCngbXhBanWjxLUt1Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", + "license": "MIT" + }, + "node_modules/@firebase/ai": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.1.tgz", + "integrity": "sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.17.tgz", + "integrity": "sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.23.tgz", + "integrity": "sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/analytics": "0.10.17", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", + "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.1.tgz", + "integrity": "sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.26.tgz", + "integrity": "sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check": "0.10.1", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-compat": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", + "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app": "0.13.2", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz", + "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.28", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.28.tgz", + "integrity": "sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth": "1.10.8", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.18.tgz", + "integrity": "sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.10.tgz", + "integrity": "sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.20.tgz", + "integrity": "sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.11.tgz", + "integrity": "sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/database": "1.0.20", + "@firebase/database-types": "1.0.15", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.15.tgz", + "integrity": "sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.12.1" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.8.0.tgz", + "integrity": "sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.53", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.53.tgz", + "integrity": "sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/firestore": "4.8.0", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.9.tgz", + "integrity": "sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.26.tgz", + "integrity": "sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/functions": "0.12.9", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/installations": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.18.tgz", + "integrity": "sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.18.tgz", + "integrity": "sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.22.tgz", + "integrity": "sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.22.tgz", + "integrity": "sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/messaging": "0.12.22", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/performance": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.7.tgz", + "integrity": "sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.20.tgz", + "integrity": "sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.7", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.5.tgz", + "integrity": "sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.18.tgz", + "integrity": "sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.6.5", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/storage": { + "version": "0.13.14", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.14.tgz", + "integrity": "sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.24.tgz", + "integrity": "sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/storage": "0.13.14", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.1.tgz", + "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==", + "license": "Apache-2.0" + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.2.tgz", + "integrity": "sha512-BQCSbjWJndCZ6bj8BSGhi4EM1TDFs9HgZXXsJ7d2kEPo+x64fB3OnQE8ffmn3vWADqeAmYBPaMPF/k6PHETXKA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.16.0.tgz", + "integrity": "sha512-7/5LRgykyOfQENcm6hDKP8SX/u9XxE5YOiWOkgkwcoO+cG8xT/cyOvp9wwN3IxfdYgpHs8CE7Nq2PKX2lNaEXw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "<4.1.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^4.4.1", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google/genai": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.8.0.tgz", + "integrity": "sha512-n3KiMFesQCy2R9iSdBIuJ0JWYQ1HZBJJkmt4PPZMGZKvlgHhBAGw1kUMyX+vsAIzprN3lK45DI755lm70wPOOg==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^9.14.2", + "ws": "^8.18.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.11.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@google/generative-ai": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz", + "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", + "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", + "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", + "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", + "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", + "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", + "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", + "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", + "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", + "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", + "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", + "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", + "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/appdmg": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@types/appdmg/-/appdmg-0.5.5.tgz", + "integrity": "sha512-G+n6DgZTZFOteITE30LnWj+HRVIGr7wMlAiLWOO02uJFWVEitaPU9JVXm9wJokkgshBawb2O1OykdcsmkkZfgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.4.tgz", + "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.3.tgz", + "integrity": "sha512-XHIrMD0NpDrNM/Ckf7XJiBbLl57KEhT3+i3yY+eWm+cqYZJQTZrKo8Y8AWKnuV5GT4scfuUGt9LzNoIx3dU1nQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.35", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/app-builder-bin": { + "version": "5.0.0-alpha.12", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz", + "integrity": "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib": { + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.0.12.tgz", + "integrity": "sha512-+/CEPH1fVKf6HowBUs6LcAIoRcjeqgvAeoSE+cl7Y7LndyQ9ViGPYibNk7wmhMHzNgHIuIbw4nWADPO+4mjgWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/asar": "3.2.18", + "@electron/fuses": "^1.8.0", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.1", + "@electron/rebuild": "3.7.0", + "@electron/universal": "2.0.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", + "chromium-pickle-js": "^0.2.0", + "config-file-ts": "0.2.8-rc1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "26.0.11", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.0.0", + "plist": "3.1.0", + "resedit": "^1.7.0", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "26.0.12", + "electron-builder-squirrel-windows": "26.0.12" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/asar": { + "version": "3.2.18", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.18.tgz", + "integrity": "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/osx-sign": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", + "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/rebuild": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz", + "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/universal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", + "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.7", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/app-builder-lib/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/app-builder-lib/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/app-builder-lib/node_modules/isbinaryfile": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.4.tgz", + "integrity": "sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/app-builder-lib/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/app-builder-lib/node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/app-builder-lib/node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/appdmg": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/appdmg/-/appdmg-0.6.6.tgz", + "integrity": "sha512-GRmFKlCG+PWbcYF4LUNonTYmy0GjguDy6Jh9WP8mpd0T6j80XIJyXBiWlD0U+MLNhqV9Nhx49Gl9GpVToulpLg==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "async": "^1.4.2", + "ds-store": "^0.1.5", + "execa": "^1.0.0", + "fs-temp": "^1.0.0", + "fs-xattr": "^0.3.0", + "image-size": "^0.7.4", + "is-my-json-valid": "^2.20.0", + "minimist": "^1.1.3", + "parse-color": "^1.0.0", + "path-exists": "^4.0.0", + "repeat-string": "^1.5.4" + }, + "bin": { + "appdmg": "bin/appdmg.js" + }, + "engines": { + "node": ">=8.5" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "license": "MIT", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/author-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz", + "integrity": "sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base32-encode": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/base32-encode/-/base32-encode-1.2.0.tgz", + "integrity": "sha512-cHFU8XeRyx0GgmoWi5qHMCVRiqU6J3MHWxVgun7jggCBUpVzm1Ir7M9dYr2whjSNc3tFeXfQ/oZjQu/4u55h9A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "to-data-view": "^1.1.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.6.0.tgz", + "integrity": "sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", + "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/bplist-creator": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.0.8.tgz", + "integrity": "sha512-Za9JKzD6fjLC16oX2wsXfc+qBEhJBJB1YPInoAQpMLhDuj5aVOv1baGeIQSq1Fr3OCqzvsoQcSBSwGId/Ja2PA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "stream-buffers": "~2.2.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "26.0.11", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.0.11.tgz", + "integrity": "sha512-xNjXfsldUEe153h1DraD0XvDOpqGR0L5eKFkdReB7eFW5HqysDZFfly4rckda6y9dF39N3pkPlOblcfHKGw+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "5.0.0-alpha.12", + "builder-util-runtime": "9.3.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.6", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.3.1.tgz", + "integrity": "sha512-2/egrNDDnRaxVwK3A+cJq6UOlqOdedGA7JPqCeJjN2Zjk1/QB/6QUi3b714ScIGS7HafFXTyzJEOr5b44I3kvQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/conf": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", + "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", + "license": "MIT", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conf/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/conf/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/config-file-ts": { + "version": "0.2.8-rc1", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", + "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.3.12", + "typescript": "^5.4.3" + } + }, + "node_modules/config-file-ts/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-zip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cross-zip/-/cross-zip-4.0.1.tgz", + "integrity": "sha512-n63i0lZ0rvQ6FXiGQ+/JFCKAUyPFhLQYJIqKaa+tSJtfKeULF/IDNDAbdnSIxgS4NTuw2b0+lj8LzfITuq+ZxQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.10" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dmg-builder": { + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.0.12.tgz", + "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.0.12", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.1.tgz", + "integrity": "sha512-GLjkduuAL7IMJg/ZnOPm9AnWKJ82mSE2tzXLaJ/6hD6DhwGfZaXG77oB8qbReyiczNxnbxQKyh0OE5mXq0bAHA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ds-store": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ds-store/-/ds-store-0.1.6.tgz", + "integrity": "sha512-kY21M6Lz+76OS3bnCzjdsJSF7LBpLYGCVfavW8TgQD2XkcqIZ86W0y9qUDZu6fp7SIZzqosMDW2zi7zVFfv4hw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bplist-creator": "~0.0.3", + "macos-alias": "~0.2.5", + "tn1150": "^0.1.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "30.5.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-30.5.1.tgz", + "integrity": "sha512-AhL7+mZ8Lg14iaNfoYTkXQ2qee8mmsQyllKdqxlpv/zrKgfxz6jNVtcRRbQtLxtF8yzcImWdfTQROpYiPumdbw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.0.12.tgz", + "integrity": "sha512-cD1kz5g2sgPTMFHjLxfMjUK5JABq3//J4jPswi93tOPFz6btzXYtK5NrDt717NRbukCUDOrrvmYVOWERlqoiXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.0.12", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", + "chalk": "^4.1.2", + "dmg-builder": "26.0.12", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "26.0.12", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.0.12.tgz", + "integrity": "sha512-kpwXM7c/ayRUbYVErQbsZ0nQZX4aLHQrPEG9C4h9vuJCXylwFH8a7Jgi2VpKIObzCXO7LKHiCw4KdioFLFOgqA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "26.0.12", + "builder-util": "26.0.11", + "electron-winstaller": "5.4.0" + } + }, + "node_modules/electron-installer-common": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/electron-installer-common/-/electron-installer-common-0.10.4.tgz", + "integrity": "sha512-8gMNPXfAqUE5CfXg8RL0vXpLE9HAaPkgLXVoHE3BMUzogMWenf4LmwQ27BdCUrEhkjrKl+igs2IHJibclR3z3Q==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@electron/asar": "^3.2.5", + "@malept/cross-spawn-promise": "^1.0.0", + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "glob": "^7.1.4", + "lodash": "^4.17.15", + "parse-author": "^2.0.0", + "semver": "^7.1.1", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "url": "https://github.com/electron-userland/electron-installer-common?sponsor=1" + }, + "optionalDependencies": { + "@types/fs-extra": "^9.0.1" + } + }, + "node_modules/electron-installer-common/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-installer-common/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-debian": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz", + "integrity": "sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin", + "linux" + ], + "dependencies": { + "@malept/cross-spawn-promise": "^1.0.0", + "debug": "^4.1.1", + "electron-installer-common": "^0.10.2", + "fs-extra": "^9.0.0", + "get-folder-size": "^2.0.1", + "lodash": "^4.17.4", + "word-wrap": "^1.2.3", + "yargs": "^16.0.2" + }, + "bin": { + "electron-installer-debian": "src/cli.js" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-installer-debian/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-installer-debian/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-installer-debian/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/electron-installer-debian/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-debian/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-installer-debian/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/electron-installer-debian/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-debian/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-dmg": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/electron-installer-dmg/-/electron-installer-dmg-5.0.1.tgz", + "integrity": "sha512-qOa1aAQdX57C+vzhDk3549dd/PRlNL4F8y736MTD1a43qptD+PvHY97Bo9gSf+OZ8iUWE7BrYSpk/FgLUe40EA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@types/appdmg": "^0.5.5", + "debug": "^4.3.2", + "minimist": "^1.2.7" + }, + "bin": { + "electron-installer-dmg": "dist/electron-installer-dmg-bin.js" + }, + "engines": { + "node": ">= 16" + }, + "optionalDependencies": { + "appdmg": "^0.6.4" + } + }, + "node_modules/electron-installer-redhat": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/electron-installer-redhat/-/electron-installer-redhat-3.4.0.tgz", + "integrity": "sha512-gEISr3U32Sgtj+fjxUAlSDo3wyGGq6OBx7rF5UdpIgbnpUvMN4W5uYb0ThpnAZ42VEJh/3aODQXHbFS4f5J3Iw==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin", + "linux" + ], + "dependencies": { + "@malept/cross-spawn-promise": "^1.0.0", + "debug": "^4.1.1", + "electron-installer-common": "^0.10.2", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "word-wrap": "^1.2.3", + "yargs": "^16.0.2" + }, + "bin": { + "electron-installer-redhat": "src/cli.js" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-installer-redhat/node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/electron-installer-redhat/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-installer-redhat/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/electron-installer-redhat/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-redhat/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-installer-redhat/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/electron-installer-redhat/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-installer-redhat/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-is-dev": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", + "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-liquid-glass": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/electron-liquid-glass/-/electron-liquid-glass-1.0.1.tgz", + "integrity": "sha512-XznNF0uDOmwvIQFGfgZM6PiQbrpXJiG4GYN6VnLzUrZwcOsQUGy51cJ5mPZCfwH+ST/ahorhLzwFEr+hsKRHXw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^8.4.0" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "node-gyp-build": "^4" + } + }, + "node_modules/electron-publish": { + "version": "26.0.11", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.0.11.tgz", + "integrity": "sha512-a8QRH0rAPIWH9WyyS5LbNvW9Ark6qe63/LqDB7vu2JXYpi0Gma5Q60Dh4tmTqhOBQt0xsrzD8qE7C+D7j+B24A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "26.0.11", + "builder-util-runtime": "9.3.1", + "chalk": "^4.1.2", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-reloader": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/electron-reloader/-/electron-reloader-1.2.3.tgz", + "integrity": "sha512-aDnACAzNg0QvQhzw7LYOx/nVS10mEtbuG6M0QQvNQcLnJEwFs6is+EGRCnM+KQlQ4KcTbdwnt07nd7ZjHpY4iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "chokidar": "^3.5.0", + "date-time": "^3.1.0", + "electron-is-dev": "^1.2.0", + "find-up": "^5.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-squirrel-startup": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/electron-squirrel-startup/-/electron-squirrel-startup-1.0.1.tgz", + "integrity": "sha512-sTfFIHGku+7PsHLJ7v0dRcZNkALrV+YEozINTW8X1nM//e5O3L+rfYuvSW00lmGHnYmUjARZulD8F2V8ISI9RA==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^2.2.0" + } + }, + "node_modules/electron-squirrel-startup/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/electron-squirrel-startup/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/electron-store": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz", + "integrity": "sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==", + "license": "MIT", + "dependencies": { + "conf": "^10.2.0", + "type-fest": "^2.17.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-updater": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.6.2.tgz", + "integrity": "sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==", + "license": "MIT", + "dependencies": { + "builder-util-runtime": "9.3.1", + "fs-extra": "^10.1.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "^7.6.3", + "tiny-typed-emitter": "^2.1.0" + } + }, + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" + } + }, + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/electron/node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/electron/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/electron/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/firebase": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.10.0.tgz", + "integrity": "sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/ai": "1.4.1", + "@firebase/analytics": "0.10.17", + "@firebase/analytics-compat": "0.2.23", + "@firebase/app": "0.13.2", + "@firebase/app-check": "0.10.1", + "@firebase/app-check-compat": "0.3.26", + "@firebase/app-compat": "0.4.2", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.10.8", + "@firebase/auth-compat": "0.5.28", + "@firebase/data-connect": "0.3.10", + "@firebase/database": "1.0.20", + "@firebase/database-compat": "2.0.11", + "@firebase/firestore": "4.8.0", + "@firebase/firestore-compat": "0.3.53", + "@firebase/functions": "0.12.9", + "@firebase/functions-compat": "0.3.26", + "@firebase/installations": "0.6.18", + "@firebase/installations-compat": "0.2.18", + "@firebase/messaging": "0.12.22", + "@firebase/messaging-compat": "0.2.22", + "@firebase/performance": "0.7.7", + "@firebase/performance-compat": "0.2.20", + "@firebase/remote-config": "0.6.5", + "@firebase/remote-config-compat": "0.2.18", + "@firebase/storage": "0.13.14", + "@firebase/storage-compat": "0.3.24", + "@firebase/util": "1.12.1" + } + }, + "node_modules/firebase-admin": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.4.0.tgz", + "integrity": "sha512-Y8DcyKK+4pl4B93ooiy1G8qvdyRMkcNFfBSh+8rbVcw4cW8dgG0VXCCTp5NUwub8sn9vSPsOwpb9tE2OuFmcfQ==", + "license": "Apache-2.0", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "google-auth-library": "^9.14.2", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "22.16.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.0.tgz", + "integrity": "sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/flora-colossus": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz", + "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "fs-extra": "^10.1.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/fmix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fmix/-/fmix-0.1.0.tgz", + "integrity": "sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "imul": "^1.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-temp": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fs-temp/-/fs-temp-1.2.1.tgz", + "integrity": "sha512-okTwLB7/Qsq82G6iN5zZJFsOfZtx2/pqrA7Hk/9fvy+c+eJS9CvgGXT2uNxwnI14BDY9L/jQPkaBgSvlKfSW9w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "random-path": "^0.1.0" + } + }, + "node_modules/fs-xattr": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/fs-xattr/-/fs-xattr-0.3.1.tgz", + "integrity": "sha512-UVqkrEW0GfDabw4C3HOrFlxKfx0eeigfRne69FxSBdHIP8Qt5Sq6Pu3RM9KmMlkygtC4pPKkj5CiPO5USnj2GA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "!win32" + ], + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "license": "MIT", + "optional": true + }, + "node_modules/galactus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", + "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "flora-colossus": "^2.0.0", + "fs-extra": "^10.1.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/gar": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz", + "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-property": "^1.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-folder-size": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz", + "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "gar": "^1.0.4", + "tiny-each-async": "2.0.3" + }, + "bin": { + "get-folder-size": "bin/get-folder-size" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-info": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz", + "integrity": "sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.1.1", + "debug": "^2.2.0", + "lodash.get": "^4.0.0", + "read-pkg-up": "^2.0.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/get-package-info/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/get-package-info/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz", + "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/@grpc/grpc-js": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", + "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-corefoundation/node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/image-size": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", + "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/imul": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/imul/-/imul-1.0.1.tgz", + "integrity": "sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-my-ip-valid": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.1.tgz", + "integrity": "sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/is-my-json-valid": { + "version": "2.20.6", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.6.tgz", + "integrity": "sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^5.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", + "license": "BSD-2-Clause" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.0.tgz", + "integrity": "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==", + "license": "MIT", + "dependencies": { + "@types/express": "^4.17.20", + "@types/jsonwebtoken": "^9.0.4", + "debug": "^4.3.4", + "jose": "^4.15.4", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "license": "MIT" + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/listr2": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", + "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", + "rfdc": "^1.3.0", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/macos-alias": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/macos-alias/-/macos-alias-0.2.12.tgz", + "integrity": "sha512-yiLHa7cfJcGRFq4FrR4tMlpNHb4Vy4mWnpajlSSIFM5k4Lv8/7BbbDLzCAVogWNl0LlLhizRp1drXv0hK9h0Yw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "nan": "^2.4.0" + } + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mem/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/murmur-32": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/murmur-32/-/murmur-32-0.2.0.tgz", + "integrity": "sha512-ZkcWZudylwF+ir3Ld1n7gL6bI2mQAzXvSobPwVtu8aYi2sbXeipeSkdcanRLzIofLcM5F53lGaKm2dk7orBi7Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "encode-utf8": "^1.0.3", + "fmix": "^0.1.0", + "imul": "^1.0.0" + } + }, + "node_modules/nan": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", + "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", + "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.115", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.115.tgz", + "integrity": "sha512-kNrFiTgG4a9JAn1LMQeLOv3MvXIPokzXziohMrMsvpYgLpdEt/mMiVYc4sGKtDfyxM5gIDF4VgrPRyCw4fHOYg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-author": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz", + "integrity": "sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "author-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-color": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz", + "integrity": "sha512-fuDHYgFHJGbpGMgw9skY/bj3HL/Jrn4l/5rSspy00DoT4RyLnDcRvPxdZ+r6OFwIsgAuhDh4I09tAId4mI12bw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "~0.5.0" + } + }, + "node_modules/parse-color/node_modules/color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==", + "dev": true, + "optional": true + }, + "node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pe-library": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-1.0.1.tgz", + "integrity": "sha512-nh39Mo1eGWmZS7y+mK/dQIqg7S1lp38DpRxkyoHf0ZcUs/HDc+yyTjuOtTvSMZHmfSLuSQaX945u05Y2Q6UWZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "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": "^2.0.0", + "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" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/random-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/random-path/-/random-path-0.1.2.tgz", + "integrity": "sha512-4jY0yoEaQ5v9StCl5kZbNIQlg1QheIDBrdkDn53EynpPb9FgO6//p3X/tgMnrC45XN6QZCzU1Xz/+pSSsJBpRw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "base32-encode": "^0.1.0 || ^1.0.0", + "murmur-32": "^0.1.0 || ^0.2.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", + "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-2.0.3.tgz", + "integrity": "sha512-oTeemxwoMuxxTYxXUwjkrOPfngTQehlv0/HoYFNkB4uzsP1Un1A9nI8JQKGOFkxpqkC7qkMs0lUsGrvUlbLNUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^1.0.1" + }, + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT", + "peer": true + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", + "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.2", + "@img/sharp-darwin-x64": "0.34.2", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.2", + "@img/sharp-linux-arm64": "0.34.2", + "@img/sharp-linux-s390x": "0.34.2", + "@img/sharp-linux-x64": "0.34.2", + "@img/sharp-linuxmusl-arm64": "0.34.2", + "@img/sharp-linuxmusl-x64": "0.34.2", + "@img/sharp-wasm32": "0.34.2", + "@img/sharp-win32-arm64": "0.34.2", + "@img/sharp-win32-ia32": "0.34.2", + "@img/sharp-win32-x64": "0.34.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", + "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-buffers": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "dev": true, + "license": "Unlicense", + "optional": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "license": "MIT", + "optional": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT", + "optional": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-outer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "license": "MIT", + "optional": true + }, + "node_modules/sudo-prompt": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz", + "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT" + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/temp/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.5.0" + } + }, + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/tiny-each-async": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz", + "integrity": "sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/tiny-typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", + "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tn1150": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tn1150/-/tn1150-0.1.0.tgz", + "integrity": "sha512-DbplOfQFkqG5IHcDyyrs/lkvSr3mPUVsFf/RbDppOshs22yTPnSJWEe6FkYd1txAwU/zcnR905ar2fi4kwF29w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "unorm": "^1.4.1" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/to-data-view": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/to-data-view/-/to-data-view-1.1.0.tgz", + "integrity": "sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", + "dev": true, + "license": "MIT or GPL-2.0", + "optional": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/username": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/username/-/username-5.1.0.tgz", + "integrity": "sha512-PCKbdWw85JsYMvmCv5GH3kXmM66rCd9m1hBEDutPNv94b/pqCMT4NtcKyeWYvLFiE8b+ha1Jdl8XAaUdPn5QTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^1.0.0", + "mem": "^4.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validator": { + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/wait-on": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.3.tgz", + "integrity": "sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==", + "license": "MIT", + "dependencies": { + "axios": "^1.8.2", + "joi": "^17.13.3", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.2" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.75", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.75.tgz", + "integrity": "sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/package.json b/package.json index 8117581..84777a5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { "name": "pickle-glass", "productName": "Glass", - "version": "0.1.2", + + "version": "0.2.2", + "description": "Cl*ely for Free", "main": "src/index.js", "scripts": { @@ -9,11 +11,14 @@ "start": "npm run build:renderer && electron-forge start", "package": "npm run build:renderer && electron-forge package", "make": "npm run build:renderer && electron-forge make", - "build": "npm run build:renderer && electron-builder --config electron-builder.yml --publish never", - "publish": "npm run build:renderer && electron-builder --config electron-builder.yml --publish always", + "build": "npm run build:all && electron-builder --config electron-builder.yml --publish never", + "build:win": "npm run build:all && electron-builder --win --x64 --publish never", + "publish": "npm run build:all && electron-builder --config electron-builder.yml --publish always", "lint": "eslint --ext .ts,.tsx,.js .", "postinstall": "electron-builder install-app-deps", "build:renderer": "node build.js", + "build:web": "cd pickleglass_web && npm run build && cd ..", + "build:all": "npm run build:renderer && npm run build:web", "watch:renderer": "node build.js --watch" }, "keywords": [ @@ -29,13 +34,13 @@ }, "license": "GPL-3.0", "dependencies": { + "@anthropic-ai/sdk": "^0.56.0", "@google/genai": "^1.8.0", "@google/generative-ai": "^0.24.1", "axios": "^1.10.0", "better-sqlite3": "^9.4.3", "cors": "^2.8.5", "dotenv": "^17.0.0", - "electron-deeplink": "^1.0.10", "electron-squirrel-startup": "^1.0.1", "electron-store": "^8.2.0", "electron-updater": "^6.6.2", @@ -47,7 +52,6 @@ "openai": "^4.70.0", "react-hot-toast": "^2.5.2", "sharp": "^0.34.2", - "sqlite3": "^5.1.7", "validator": "^13.11.0", "wait-on": "^8.0.3", "ws": "^8.18.0" @@ -69,7 +73,6 @@ "esbuild": "^0.25.5" }, "optionalDependencies": { - "@img/sharp-darwin-x64": "^0.34.2", - "@img/sharp-libvips-darwin-x64": "^1.1.0" + "electron-liquid-glass": "^1.0.1" } } diff --git a/pickleglass_web/backend_node/db.js b/pickleglass_web/backend_node/db.js deleted file mode 100644 index d59636a..0000000 --- a/pickleglass_web/backend_node/db.js +++ /dev/null @@ -1,33 +0,0 @@ -const path = require('path'); -const databaseInitializer = require('../../src/common/services/databaseInitializer'); -const Database = require('better-sqlite3'); - -const dbPath = databaseInitializer.getDatabasePath(); -const db = new Database(dbPath); - -db.pragma('journal_mode = WAL'); - -// The schema is now managed by the main Electron process on startup. -// This file can assume the schema is correct and up-to-date. - -const defaultPresets = [ - ['school', 'School', 'You are a school and lecture assistant. Your goal is to help the user, a student, understand academic material and answer questions.\n\nWhenever a question appears on the user\'s screen or is asked aloud, you provide a direct, step-by-step answer, showing all necessary reasoning or calculations.\n\nIf the user is watching a lecture or working through new material, you offer concise explanations of key concepts and clarify definitions as they come up.', 1], - ['meetings', 'Meetings', 'You are a meeting assistant. Your goal is to help the user capture key information during meetings and follow up effectively.\n\nYou help capture meeting notes, track action items, identify key decisions, and summarize important points discussed during meetings.', 1], - ['sales', 'Sales', 'You are a real-time AI sales assistant, and your goal is to help the user close deals during sales interactions.\n\nYou provide real-time sales support, suggest responses to objections, help identify customer needs, and recommend strategies to advance deals.', 1], - ['recruiting', 'Recruiting', 'You are a recruiting assistant. Your goal is to help the user interview candidates and evaluate talent effectively.\n\nYou help evaluate candidates, suggest interview questions, analyze responses, and provide insights about candidate fit for positions.', 1], - ['customer-support', 'Customer Support', 'You are a customer support assistant. Your goal is to help resolve customer issues efficiently and thoroughly.\n\nYou help diagnose customer problems, suggest solutions, provide step-by-step troubleshooting guidance, and ensure customer satisfaction.', 1], -]; - -const stmt = db.prepare(` -INSERT OR IGNORE INTO prompt_presets (id, uid, title, prompt, is_default, created_at) -VALUES (@id, 'default_user', @title, @prompt, @is_default, strftime('%s','now')); -`); -db.transaction(() => defaultPresets.forEach(([id, title, prompt, is_default]) => stmt.run({ id, title, prompt, is_default })))(); - -const defaultUserStmt = db.prepare(` -INSERT OR IGNORE INTO users (uid, display_name, email, created_at) -VALUES ('default_user', 'Default User', 'contact@pickle.com', strftime('%s','now')); -`); -defaultUserStmt.run(); - -module.exports = db; diff --git a/pickleglass_web/backend_node/index.js b/pickleglass_web/backend_node/index.js index a60d6f0..a466490 100644 --- a/pickleglass_web/backend_node/index.js +++ b/pickleglass_web/backend_node/index.js @@ -1,9 +1,9 @@ const express = require('express'); const cors = require('cors'); -const db = require('./db'); +// const db = require('./db'); // No longer needed const { identifyUser } = require('./middleware/auth'); -function createApp() { +function createApp(eventBridge) { const app = express(); const webUrl = process.env.pickleglass_WEB_URL || 'http://localhost:3000'; @@ -20,6 +20,11 @@ function createApp() { res.json({ message: "pickleglass API is running" }); }); + app.use((req, res, next) => { + req.bridge = eventBridge; + next(); + }); + app.use('/api', identifyUser); app.use('/api/auth', require('./routes/auth')); diff --git a/pickleglass_web/backend_node/ipcBridge.js b/pickleglass_web/backend_node/ipcBridge.js new file mode 100644 index 0000000..805403c --- /dev/null +++ b/pickleglass_web/backend_node/ipcBridge.js @@ -0,0 +1,35 @@ +const crypto = require('crypto'); + +function ipcRequest(req, channel, payload) { + return new Promise((resolve, reject) => { + // μ¦‰μ‹œ λΈŒλ¦¬μ§€ μƒνƒœ 확인 - 문제있으면 λ°”λ‘œ μ‹€νŒ¨ + if (!req.bridge || typeof req.bridge.emit !== 'function') { + reject(new Error('IPC bridge is not available')); + return; + } + + const responseChannel = `${channel}-${crypto.randomUUID()}`; + + req.bridge.once(responseChannel, (response) => { + if (!response) { + reject(new Error(`No response received from ${channel}`)); + return; + } + + if (response.success) { + resolve(response.data); + } else { + reject(new Error(response.error || `IPC request to ${channel} failed`)); + } + }); + + try { + req.bridge.emit('web-data-request', channel, responseChannel, payload); + } catch (error) { + req.bridge.removeAllListeners(responseChannel); + reject(new Error(`Failed to emit IPC request: ${error.message}`)); + } + }); +} + +module.exports = { ipcRequest }; \ No newline at end of file diff --git a/pickleglass_web/backend_node/jwt.js b/pickleglass_web/backend_node/jwt.js deleted file mode 100644 index abb79ad..0000000 --- a/pickleglass_web/backend_node/jwt.js +++ /dev/null @@ -1,13 +0,0 @@ -const jwt = require('jsonwebtoken'); -const SECRET = process.env.JWT_SECRET_KEY || 'change-me'; -const EXPIRE = 60 * 24; // minutes - -exports.sign = (sub, extra = {}) => jwt.sign({ sub, ...extra }, SECRET, { algorithm: 'HS256', expiresIn: `${EXPIRE}m` }); - -exports.verify = token => { - try { - return jwt.verify(token, SECRET).sub; - } catch { - return null; - } -}; diff --git a/pickleglass_web/backend_node/middleware/auth.js b/pickleglass_web/backend_node/middleware/auth.js index f20b0eb..cb7a265 100644 --- a/pickleglass_web/backend_node/middleware/auth.js +++ b/pickleglass_web/backend_node/middleware/auth.js @@ -1,5 +1,3 @@ -const { verify } = require('../jwt'); - function identifyUser(req, res, next) { const userId = req.get('X-User-ID'); diff --git a/pickleglass_web/backend_node/routes/auth.js b/pickleglass_web/backend_node/routes/auth.js index 5dbe34d..6ccdc3f 100644 --- a/pickleglass_web/backend_node/routes/auth.js +++ b/pickleglass_web/backend_node/routes/auth.js @@ -1,19 +1,24 @@ const express = require('express'); -const db = require('../db'); const router = express.Router(); +const { ipcRequest } = require('../ipcBridge'); -router.get('/status', (req, res) => { - const user = db.prepare('SELECT uid, display_name FROM users WHERE uid = ?').get('default_user'); - if (!user) { - return res.status(500).json({ error: 'Default user not initialized' }); - } - res.json({ - authenticated: true, - user: { - id: user.uid, - name: user.display_name +router.get('/status', async (req, res) => { + try { + const user = await ipcRequest(req, 'get-user-profile'); + if (!user) { + return res.status(500).json({ error: 'Default user not initialized' }); } - }); + res.json({ + authenticated: true, + user: { + id: user.uid, + name: user.display_name + } + }); + } catch (error) { + console.error('Failed to get auth status via IPC:', error); + res.status(500).json({ error: 'Failed to retrieve auth status' }); + } }); module.exports = router; diff --git a/pickleglass_web/backend_node/routes/conversations.js b/pickleglass_web/backend_node/routes/conversations.js index 903b446..7ea9d8a 100644 --- a/pickleglass_web/backend_node/routes/conversations.js +++ b/pickleglass_web/backend_node/routes/conversations.js @@ -1,121 +1,54 @@ const express = require('express'); -const db = require('../db'); const router = express.Router(); -const crypto = require('crypto'); -const validator = require('validator'); +const { ipcRequest } = require('../ipcBridge'); -router.get('/', (req, res) => { +router.get('/', async (req, res) => { try { - const sessions = db.prepare( - "SELECT id, uid, title, session_type, started_at, ended_at, sync_state, updated_at FROM sessions WHERE uid = ? ORDER BY started_at DESC" - ).all(req.uid); + const sessions = await ipcRequest(req, 'get-sessions'); res.json(sessions); } catch (error) { - console.error('Failed to get sessions:', error); + console.error('Failed to get sessions via IPC:', error); res.status(500).json({ error: 'Failed to retrieve sessions' }); } }); -router.post('/', (req, res) => { - const { title } = req.body; - const sessionId = crypto.randomUUID(); - const now = Math.floor(Date.now() / 1000); - +router.post('/', async (req, res) => { try { - db.prepare( - `INSERT INTO sessions (id, uid, title, started_at, updated_at) - VALUES (?, ?, ?, ?, ?)` - ).run(sessionId, req.uid, title || 'New Conversation', now, now); - - res.status(201).json({ id: sessionId, message: 'Session created successfully' }); + const result = await ipcRequest(req, 'create-session', req.body); + res.status(201).json({ ...result, message: 'Session created successfully' }); } catch (error) { - console.error('Failed to create session:', error); + console.error('Failed to create session via IPC:', error); res.status(500).json({ error: 'Failed to create session' }); } }); -router.get('/:session_id', (req, res) => { - const { session_id } = req.params; +router.get('/:session_id', async (req, res) => { try { - const session = db.prepare("SELECT * FROM sessions WHERE id = ?").get(session_id); - if (!session) { + const details = await ipcRequest(req, 'get-session-details', req.params.session_id); + if (!details) { return res.status(404).json({ error: 'Session not found' }); } - - const transcripts = db.prepare("SELECT * FROM transcripts WHERE session_id = ? ORDER BY start_at ASC").all(session_id); - const ai_messages = db.prepare("SELECT * FROM ai_messages WHERE session_id = ? ORDER BY sent_at ASC").all(session_id); - const summary = db.prepare("SELECT * FROM summaries WHERE session_id = ?").get(session_id); - - res.json({ - session, - transcripts, - ai_messages, - summary: summary || null - }); + res.json(details); } catch (error) { - console.error(`Failed to get session ${session_id}:`, error); + console.error(`Failed to get session details via IPC for ${req.params.session_id}:`, error); res.status(500).json({ error: 'Failed to retrieve session details' }); } }); -router.delete('/:session_id', (req, res) => { - const { session_id } = req.params; - - const session = db.prepare("SELECT id FROM sessions WHERE id = ?").get(session_id); - if (!session) { - return res.status(404).json({ error: 'Session not found' }); - } - +router.delete('/:session_id', async (req, res) => { try { - db.transaction(() => { - db.prepare("DELETE FROM transcripts WHERE session_id = ?").run(session_id); - db.prepare("DELETE FROM ai_messages WHERE session_id = ?").run(session_id); - db.prepare("DELETE FROM summaries WHERE session_id = ?").run(session_id); - db.prepare("DELETE FROM sessions WHERE id = ?").run(session_id); - })(); + await ipcRequest(req, 'delete-session', req.params.session_id); res.status(200).json({ message: 'Session deleted successfully' }); } catch (error) { - console.error(`Failed to delete session ${session_id}:`, error); + console.error(`Failed to delete session via IPC for ${req.params.session_id}:`, error); res.status(500).json({ error: 'Failed to delete session' }); } }); +// The search functionality will be more complex to move to IPC. +// For now, we can disable it or leave it as is, knowing it's a future task. router.get('/search', (req, res) => { - const { q } = req.query; - if (!q || !validator.isLength(q, { min: 3 })) { - return res.status(400).json({ error: 'Query parameter "q" is required' }); - } - // Sanitize and validate input - const sanitizedQuery = validator.escape(q.trim()); // Escapes HTML and special chars - if (sanitizedQuery.length === 0 || sanitizedQuery.length > 255) { - return res.status(400).json({ error: 'Query parameter "q" must be between 3 and 255 characters' }); - } - try { - const searchQuery = `%${sanitizedQuery}%`; - const sessionIds = db.prepare(` - SELECT DISTINCT session_id FROM ( - SELECT session_id FROM transcripts WHERE text LIKE ? - UNION - SELECT session_id FROM ai_messages WHERE content LIKE ? - UNION - SELECT session_id FROM summaries WHERE text LIKE ? OR tldr LIKE ? - ) - `).all(searchQuery, searchQuery, searchQuery, searchQuery).map(row => row.session_id); - - if (sessionIds.length === 0) { - return res.json([]); - } - - const placeholders = sessionIds.map(() => '?').join(','); - const sessions = db.prepare( - `SELECT id, uid, title, started_at, ended_at, sync_state, updated_at FROM sessions WHERE id IN (${placeholders}) ORDER BY started_at DESC` - ).all(sessionIds); - - res.json(sessions); - } catch (error) { - console.error('Search failed:', error); - res.status(500).json({ error: 'Failed to perform search' }); - } + res.status(501).json({ error: 'Search not implemented for IPC bridge yet.' }); }); module.exports = router; \ No newline at end of file diff --git a/pickleglass_web/backend_node/routes/presets.js b/pickleglass_web/backend_node/routes/presets.js index 8812f7e..c9c3f47 100644 --- a/pickleglass_web/backend_node/routes/presets.js +++ b/pickleglass_web/backend_node/routes/presets.js @@ -1,85 +1,43 @@ const express = require('express'); -const crypto = require('crypto'); -const db = require('../db'); const router = express.Router(); +const { ipcRequest } = require('../ipcBridge'); -router.get('/', (req, res) => { +router.get('/', async (req, res) => { try { - const presets = db.prepare( - `SELECT * FROM prompt_presets - WHERE uid = ? OR is_default = 1 - ORDER BY is_default DESC, title ASC` - ).all(req.uid); + const presets = await ipcRequest(req, 'get-presets'); res.json(presets); } catch (error) { - console.error('Failed to get presets:', error); + console.error('Failed to get presets via IPC:', error); res.status(500).json({ error: 'Failed to retrieve presets' }); } }); -router.post('/', (req, res) => { - const { title, prompt } = req.body; - if (!title || !prompt) { - return res.status(400).json({ error: 'Title and prompt are required' }); - } - - const presetId = crypto.randomUUID(); - const now = Math.floor(Date.now() / 1000); - +router.post('/', async (req, res) => { try { - db.prepare( - `INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state) - VALUES (?, ?, ?, ?, 0, ?, 'dirty')` - ).run(presetId, req.uid, title, prompt, now); - - res.status(201).json({ id: presetId, message: 'Preset created successfully' }); + const result = await ipcRequest(req, 'create-preset', req.body); + res.status(201).json({ ...result, message: 'Preset created successfully' }); } catch (error) { - console.error('Failed to create preset:', error); + console.error('Failed to create preset via IPC:', error); res.status(500).json({ error: 'Failed to create preset' }); } }); -router.put('/:id', (req, res) => { - const { id } = req.params; - const { title, prompt } = req.body; - if (!title || !prompt) { - return res.status(400).json({ error: 'Title and prompt are required' }); - } - +router.put('/:id', async (req, res) => { try { - const result = db.prepare( - `UPDATE prompt_presets - SET title = ?, prompt = ?, sync_state = 'dirty' - WHERE id = ? AND uid = ? AND is_default = 0` - ).run(title, prompt, id, req.uid); - - if (result.changes === 0) { - return res.status(404).json({ error: "Preset not found or you don't have permission to edit it." }); - } - + await ipcRequest(req, 'update-preset', { id: req.params.id, data: req.body }); res.json({ message: 'Preset updated successfully' }); } catch (error) { - console.error('Failed to update preset:', error); + console.error('Failed to update preset via IPC:', error); res.status(500).json({ error: 'Failed to update preset' }); } }); -router.delete('/:id', (req, res) => { - const { id } = req.params; - +router.delete('/:id', async (req, res) => { try { - const result = db.prepare( - `DELETE FROM prompt_presets - WHERE id = ? AND uid = ? AND is_default = 0` - ).run(id, req.uid); - - if (result.changes === 0) { - return res.status(404).json({ error: "Preset not found or you don't have permission to delete it." }); - } - + await ipcRequest(req, 'delete-preset', req.params.id); res.json({ message: 'Preset deleted successfully' }); } catch (error) { - console.error('Failed to delete preset:', error); + console.error('Failed to delete preset via IPC:', error); res.status(500).json({ error: 'Failed to delete preset' }); } }); diff --git a/pickleglass_web/backend_node/routes/user.js b/pickleglass_web/backend_node/routes/user.js index 8e5ff19..d31a9a8 100644 --- a/pickleglass_web/backend_node/routes/user.js +++ b/pickleglass_web/backend_node/routes/user.js @@ -1,144 +1,87 @@ const express = require('express'); -const db = require('../db'); const router = express.Router(); +const { ipcRequest } = require('../ipcBridge'); -router.put('/profile', (req, res) => { - const { displayName } = req.body; - if (!displayName) return res.status(400).json({ error: 'displayName is required' }); - +router.put('/profile', async (req, res) => { try { - db.prepare("UPDATE users SET display_name = ? WHERE uid = ?").run(displayName, req.uid); + await ipcRequest(req, 'update-user-profile', req.body); res.json({ message: 'Profile updated successfully' }); } catch (error) { - console.error('Failed to update profile:', error); + console.error('Failed to update profile via IPC:', error); res.status(500).json({ error: 'Failed to update profile' }); } }); -router.get('/profile', (req, res) => { +router.get('/profile', async (req, res) => { try { - const user = db.prepare('SELECT uid, display_name, email FROM users WHERE uid = ?').get(req.uid); + const user = await ipcRequest(req, 'get-user-profile'); if (!user) return res.status(404).json({ error: 'User not found' }); res.json(user); } catch (error) { - console.error('Failed to get profile:', error); + console.error('Failed to get profile via IPC:', error); res.status(500).json({ error: 'Failed to get profile' }); } }); -router.post('/find-or-create', (req, res) => { - const { uid, displayName, email } = req.body; - if (!uid || !displayName || !email) { - return res.status(400).json({ error: 'uid, displayName, and email are required' }); - } - +router.post('/find-or-create', async (req, res) => { try { - const now = Math.floor(Date.now() / 1000); - db.prepare( - `INSERT INTO users (uid, display_name, email, created_at) - VALUES (?, ?, ?, ?) - ON CONFLICT(uid) DO NOTHING` - ).run(uid, displayName, email, now); + console.log('[API] find-or-create request received:', req.body); - const user = db.prepare('SELECT * FROM users WHERE uid = ?').get(uid); + if (!req.body || !req.body.uid) { + return res.status(400).json({ error: 'User data with uid is required' }); + } + + const user = await ipcRequest(req, 'find-or-create-user', req.body); + console.log('[API] find-or-create response:', user); res.status(200).json(user); - } catch (error) { - console.error('Failed to find or create user:', error); - res.status(500).json({ error: 'Failed to find or create user' }); + console.error('Failed to find or create user via IPC:', error); + console.error('Request body:', req.body); + res.status(500).json({ + error: 'Failed to find or create user', + details: error.message + }); } }); -router.post('/api-key', (req, res) => { - const { apiKey } = req.body; - if (typeof apiKey !== 'string') { - return res.status(400).json({ error: 'API key must be a string' }); - } - +router.post('/api-key', async (req, res) => { try { - db.prepare("UPDATE users SET api_key = ? WHERE uid = ?").run(apiKey, req.uid); - res.json({ message: 'API key saved successfully' }); + await ipcRequest(req, 'save-api-key', req.body.apiKey); + res.json({ message: 'API key saved successfully' }); } catch (error) { - console.error('Failed to save API key:', error); + console.error('Failed to save API key via IPC:', error); res.status(500).json({ error: 'Failed to save API key' }); } }); -router.get('/api-key-status', (req, res) => { +router.get('/api-key-status', async (req, res) => { try { - const row = db.prepare('SELECT api_key FROM users WHERE uid = ?').get(req.uid); - if (!row) { - return res.status(404).json({ error: 'User not found' }); - } - res.json({ hasApiKey: !!row.api_key && row.api_key.length > 0 }); + const status = await ipcRequest(req, 'check-api-key-status'); + res.json(status); } catch (error) { - console.error('Failed to get API key status:', error); + console.error('Failed to get API key status via IPC:', error); res.status(500).json({ error: 'Failed to get API key status' }); } }); -router.delete('/profile', (req, res) => { +router.delete('/profile', async (req, res) => { try { - const user = db.prepare('SELECT uid FROM users WHERE uid = ?').get(req.uid); - if (!user) { - return res.status(404).json({ error: 'User not found' }); - } - - const userSessions = db.prepare('SELECT id FROM sessions WHERE uid = ?').all(user.uid); - const sessionIds = userSessions.map(s => s.id); - - db.transaction(() => { - if (sessionIds.length > 0) { - const placeholders = sessionIds.map(() => '?').join(','); - db.prepare(`DELETE FROM transcripts WHERE session_id IN (${placeholders})`).run(...sessionIds); - db.prepare(`DELETE FROM ai_messages WHERE session_id IN (${placeholders})`).run(...sessionIds); - db.prepare(`DELETE FROM summaries WHERE session_id IN (${placeholders})`).run(...sessionIds); - db.prepare(`DELETE FROM sessions WHERE uid = ?`).run(user.uid); - } - db.prepare('DELETE FROM prompt_presets WHERE uid = ?').run(user.uid); - db.prepare('DELETE FROM users WHERE uid = ?').run(user.uid); - })(); - + await ipcRequest(req, 'delete-account'); res.status(200).json({ message: 'User account and all data deleted successfully.' }); - } catch (error) { - console.error('Failed to delete user account:', error); + console.error('Failed to delete user account via IPC:', error); res.status(500).json({ error: 'Failed to delete user account' }); } }); -async function getUserBatchData(req, res) { - const { include = 'profile,presets,sessions' } = req.query; - +router.get('/batch', async (req, res) => { try { - const includes = include.split(',').map(item => item.trim()); - const result = {}; - - if (includes.includes('profile')) { - const user = db.prepare('SELECT uid, display_name, email FROM users WHERE uid = ?').get(req.uid); - result.profile = user || null; - } - - if (includes.includes('presets')) { - const presets = db.prepare('SELECT * FROM prompt_presets WHERE uid = ? OR is_default = 1').all(req.uid); - result.presets = presets || []; - } - - if (includes.includes('sessions')) { - const recent_sessions = db.prepare( - "SELECT id, title, started_at, updated_at FROM sessions WHERE uid = ? ORDER BY updated_at DESC LIMIT 10" - ).all(req.uid); - result.sessions = recent_sessions || []; - } - - res.json(result); - - } catch (error) { - console.error('Failed to get batch data:', error); + const result = await ipcRequest(req, 'get-batch-data', req.query.include); + res.json(result); + } catch(error) { + console.error('Failed to get batch data via IPC:', error); res.status(500).json({ error: 'Failed to get batch data' }); } -} - -router.get('/batch', getUserBatchData); +}); module.exports = router; diff --git a/pickleglass_web/package-lock.json b/pickleglass_web/package-lock.json new file mode 100644 index 0000000..f1d6fa0 --- /dev/null +++ b/pickleglass_web/package-lock.json @@ -0,0 +1,6976 @@ +{ + "name": "pickleglass-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pickleglass-frontend", + "version": "0.1.0", + "dependencies": { + "@headlessui/react": "^1.7.17", + "autoprefixer": "^10.4.16", + "axios": "^1.6.0", + "firebase": "^11.10.0", + "lucide-react": "^0.294.0", + "next": "^14.2.30", + "postcss": "^8.4.32", + "react": "^18", + "react-dom": "^18", + "react-hot-toast": "^2.5.2", + "tailwindcss": "^3.3.0" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.0.4", + "typescript": "^5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emnapi/core": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz", + "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz", + "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz", + "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@firebase/ai": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.4.1.tgz", + "integrity": "sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.17.tgz", + "integrity": "sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.23", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.23.tgz", + "integrity": "sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/analytics": "0.10.17", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", + "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.1.tgz", + "integrity": "sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.26.tgz", + "integrity": "sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check": "0.10.1", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/app-compat": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", + "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app": "0.13.2", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.28", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.28.tgz", + "integrity": "sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth": "1.10.8", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-compat/node_modules/@firebase/auth": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz", + "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.18.tgz", + "integrity": "sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.10.tgz", + "integrity": "sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.20.tgz", + "integrity": "sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.11.tgz", + "integrity": "sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/database": "1.0.20", + "@firebase/database-types": "1.0.15", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.15.tgz", + "integrity": "sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.12.1" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.8.0.tgz", + "integrity": "sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.53", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.53.tgz", + "integrity": "sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/firestore": "4.8.0", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.9.tgz", + "integrity": "sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.18", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.26.tgz", + "integrity": "sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/functions": "0.12.9", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/installations": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.18.tgz", + "integrity": "sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.18.tgz", + "integrity": "sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.22.tgz", + "integrity": "sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.1", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.22.tgz", + "integrity": "sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/messaging": "0.12.22", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/performance": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.7.tgz", + "integrity": "sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.20", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.20.tgz", + "integrity": "sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.7", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.5.tgz", + "integrity": "sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/installations": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.18.tgz", + "integrity": "sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.6.5", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==", + "license": "Apache-2.0" + }, + "node_modules/@firebase/storage": { + "version": "0.13.14", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.14.tgz", + "integrity": "sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.24.tgz", + "integrity": "sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/storage": "0.13.14", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "license": "Apache-2.0", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.1.tgz", + "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==", + "license": "Apache-2.0" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@headlessui/react": { + "version": "1.7.19", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", + "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==", + "license": "MIT", + "dependencies": { + "@tanstack/react-virtual": "^3.0.0-beta.60", + "client-only": "^0.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", + "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@next/env": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.30.tgz", + "integrity": "sha512-KBiBKrDY6kxTQWGzKjQB7QirL3PiiOkV7KW98leHFjtVRKtft76Ra5qSA/SL75xT44dp6hOcqiiJ6iievLOYug==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.0.4.tgz", + "integrity": "sha512-U3qMNHmEZoVmHA0j/57nRfi3AscXNvkOnxDmle/69Jz/G0o/gWjXTDdlgILZdrxQ0Lw/jv2mPW8PGy0EGIHXhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "7.1.7" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.30.tgz", + "integrity": "sha512-EAqfOTb3bTGh9+ewpO/jC59uACadRHM6TSA9DdxJB/6gxOpyV+zrbqeXiFTDy9uV6bmipFDkfpAskeaDcO+7/g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.30.tgz", + "integrity": "sha512-TyO7Wz1IKE2kGv8dwQ0bmPL3s44EKVencOqwIY69myoS3rdpO1NPg5xPM5ymKu7nfX4oYJrpMxv8G9iqLsnL4A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.30.tgz", + "integrity": "sha512-I5lg1fgPJ7I5dk6mr3qCH1hJYKJu1FsfKSiTKoYwcuUf53HWTrEkwmMI0t5ojFKeA6Vu+SfT2zVy5NS0QLXV4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.30.tgz", + "integrity": "sha512-8GkNA+sLclQyxgzCDs2/2GSwBc92QLMrmYAmoP2xehe5MUKBLB2cgo34Yu242L1siSkwQkiV4YLdCnjwc/Micw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.30.tgz", + "integrity": "sha512-8Ly7okjssLuBoe8qaRCcjGtcMsv79hwzn/63wNeIkzJVFVX06h5S737XNr7DZwlsbTBDOyI6qbL2BJB5n6TV/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.30.tgz", + "integrity": "sha512-dBmV1lLNeX4mR7uI7KNVHsGQU+OgTG5RGFPi3tBJpsKPvOPtg9poyav/BYWrB3GPQL4dW5YGGgalwZ79WukbKQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.30.tgz", + "integrity": "sha512-6MMHi2Qc1Gkq+4YLXAgbYslE1f9zMGBikKMdmQRHXjkGPot1JY3n5/Qrbg40Uvbi8//wYnydPnyvNhI1DMUW1g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.30.tgz", + "integrity": "sha512-pVZMnFok5qEX4RT59mK2hEVtJX+XFfak+/rjHpyFh7juiT52r177bfFKhnlafm0UOSldhXjj32b+LZIOdswGTg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.30.tgz", + "integrity": "sha512-4KCo8hMZXMjpTzs3HOqOGYYwAXymXIy7PEPAXNEcEOyKqkjiDlECumrWziy+JEF0Oi4ILHGxzgQ3YiMGG2t/Lg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", + "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", + "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.4.tgz", + "integrity": "sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.23", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", + "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.0.tgz", + "integrity": "sha512-LRw5BW29sYj9NsQC6QoqeLVQhEa+BwVINYyMlcve+6stwdBsSt5UB7zw4UZB4+4PNqIVilHoMaPWCb/KhABHQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.0.tgz", + "integrity": "sha512-zYX8D2zcWCAHqghA8tPjbp7LwjVXbIZP++mpU/Mrf5jUVlk3BWIxkeB8yYzZi5GpFSlqMcRZQxQqbMI0c2lASQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.0.tgz", + "integrity": "sha512-YsYOT049hevAY/lTYD77GhRs885EXPeAfExG5KenqMJ417nYLS2N/kpRpYbABhFZBVQn+2uRPasTe4ypmYoo3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.0.tgz", + "integrity": "sha512-PSjvk3OZf1aZImdGY5xj9ClFG3bC4gnSSYWrt+id0UAv+GwwVldhpMFjAga8SpMo2T1GjV9UKwM+QCsQCQmtdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.0.tgz", + "integrity": "sha512-KC/iFaEN/wsTVYnHClyHh5RSYA9PpuGfqkFua45r4sweXpC0KHZ+BYY7ikfcGPt5w1lMpR1gneFzuqWLQxsRKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.0.tgz", + "integrity": "sha512-CDh/0v8uot43cB4yKtDL9CVY8pbPnMV0dHyQCE4lFz6PW/+9tS0i9eqP5a91PAqEBVMqH1ycu+k8rP6wQU846w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.0.tgz", + "integrity": "sha512-+TE7epATDSnvwr3L/hNHX3wQ8KQYB+jSDTdywycg3qDqvavRP8/HX9qdq/rMcnaRDn4EOtallb3vL/5wCWGCkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.0.tgz", + "integrity": "sha512-VBAYGg3VahofpQ+L4k/ZO8TSICIbUKKTaMYOWHWfuYBFqPbSkArZZLezw3xd27fQkxX4BaLGb/RKnW0dH9Y/UA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.0.tgz", + "integrity": "sha512-9IgGFUUb02J1hqdRAHXpZHIeUHRrbnGo6vrRbz0fREH7g+rzQy53/IBSyadZ/LG5iqMxukriNPu4hEMUn+uWEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.0.tgz", + "integrity": "sha512-LR4iQ/LPjMfivpL2bQ9kmm3UnTas3U+umcCnq/CV7HAkukVdHxrDD1wwx74MIWbbgzQTLPYY7Ur2MnnvkYJCBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.0.tgz", + "integrity": "sha512-HCupFQwMrRhrOg7YHrobbB5ADg0Q8RNiuefqMHVsdhEy9lLyXm/CxsCXeLJdrg27NAPsCaMDtdlm8Z2X8x91Tg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.0.tgz", + "integrity": "sha512-Ckxy76A5xgjWa4FNrzcKul5qFMWgP5JSQ5YKd0XakmWOddPLSkQT+uAvUpQNnFGNbgKzv90DyQlxPDYPQ4nd6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.0.tgz", + "integrity": "sha512-HfO0PUCCRte2pMJmVyxPI+eqT7KuV3Fnvn2RPvMe5mOzb2BJKf4/Vth8sSt9cerQboMaTVpbxyYjjLBWIuI5BQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.0.tgz", + "integrity": "sha512-9PZdjP7tLOEjpXHS6+B/RNqtfVUyDEmaViPOuSqcbomLdkJnalt5RKQ1tr2m16+qAufV0aDkfhXtoO7DQos/jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.0.tgz", + "integrity": "sha512-qkE99ieiSKMnFJY/EfyGKVtNra52/k+lVF/PbO4EL5nU6AdvG4XhtJ+WHojAJP7ID9BNIra/yd75EHndewNRfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.0.tgz", + "integrity": "sha512-MjXek8UL9tIX34gymvQLecz2hMaQzOlaqYJJBomwm1gsvK2F7hF+YqJJ2tRyBDTv9EZJGMt4KlKkSD/gZWCOiw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.0.tgz", + "integrity": "sha512-9LT6zIGO7CHybiQSh7DnQGwFMZvVr0kUjah6qQfkH2ghucxPV6e71sUXJdSM4Ba0MaGE6DC/NwWf7mJmc3DAng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.0.tgz", + "integrity": "sha512-HYchBYOZ7WN266VjoGm20xFv5EonG/ODURRgwl9EZT7Bq1nLEs6VKJddzfFdXEAho0wfFlt8L/xIiE29Pmy1RA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.0.tgz", + "integrity": "sha512-+oLKLHw3I1UQo4MeHfoLYF+e6YBa8p5vYUw3Rgt7IDzCs+57vIZqQlIo62NDpYM0VG6BjWOwnzBczMvbtH8hag==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.180", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz", + "integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-next": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.0.4.tgz", + "integrity": "sha512-9/xbOHEQOmQtqvQ1UsTQZpnA7SlDMBtuKJ//S4JnoyK3oGLhILKXdBgu/UO7lQo/2xOykQULS1qQ6p2+EpHgAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "14.0.4", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/firebase": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.10.0.tgz", + "integrity": "sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/ai": "1.4.1", + "@firebase/analytics": "0.10.17", + "@firebase/analytics-compat": "0.2.23", + "@firebase/app": "0.13.2", + "@firebase/app-check": "0.10.1", + "@firebase/app-check-compat": "0.3.26", + "@firebase/app-compat": "0.4.2", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.10.8", + "@firebase/auth-compat": "0.5.28", + "@firebase/data-connect": "0.3.10", + "@firebase/database": "1.0.20", + "@firebase/database-compat": "2.0.11", + "@firebase/firestore": "4.8.0", + "@firebase/firestore-compat": "0.3.53", + "@firebase/functions": "0.12.9", + "@firebase/functions-compat": "0.3.26", + "@firebase/installations": "0.6.18", + "@firebase/installations-compat": "0.2.18", + "@firebase/messaging": "0.12.22", + "@firebase/messaging-compat": "0.2.22", + "@firebase/performance": "0.7.7", + "@firebase/performance-compat": "0.2.20", + "@firebase/remote-config": "0.6.5", + "@firebase/remote-config-compat": "0.2.18", + "@firebase/storage": "0.13.14", + "@firebase/storage-compat": "0.3.24", + "@firebase/util": "1.12.1" + } + }, + "node_modules/firebase/node_modules/@firebase/auth": { + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.8.tgz", + "integrity": "sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==", + "license": "Apache-2.0", + "dependencies": { + "@firebase/component": "0.6.18", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/lucide-react": { + "version": "0.294.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.294.0.tgz", + "integrity": "sha512-V7o0/VECSGbLHn3/1O67FUgBwWB+hmzshrgDVRJQhMh8uj5D3HBuIvhuAmQTtlupILSplwIZg5FTc4tTKMA2SA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.0.tgz", + "integrity": "sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "14.2.30", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.30.tgz", + "integrity": "sha512-+COdu6HQrHHFQ1S/8BBsCag61jZacmvbuL2avHvQFbWa2Ox7bE+d8FyNgxRLjXQ5wtPyQwEmk85js/AuaG2Sbg==", + "license": "MIT", + "dependencies": { + "@next/env": "14.2.30", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.30", + "@next/swc-darwin-x64": "14.2.30", + "@next/swc-linux-arm64-gnu": "14.2.30", + "@next/swc-linux-arm64-musl": "14.2.30", + "@next/swc-linux-x64-gnu": "14.2.30", + "@next/swc-linux-x64-musl": "14.2.30", + "@next/swc-win32-arm64-msvc": "14.2.30", + "@next/swc-win32-ia32-msvc": "14.2.30", + "@next/swc-win32-x64-msvc": "14.2.30" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", + "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.0.tgz", + "integrity": "sha512-uw3hCGO/RdAEAb4zgJ3C/v6KIAFFOtBoxR86b2Ejc5TnH7HrhTWJR2o0A9ullC3eWMegKQCw/arQ/JivywQzkg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.0", + "@unrs/resolver-binding-android-arm64": "1.11.0", + "@unrs/resolver-binding-darwin-arm64": "1.11.0", + "@unrs/resolver-binding-darwin-x64": "1.11.0", + "@unrs/resolver-binding-freebsd-x64": "1.11.0", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.0", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.0", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.0", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.0", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.0", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.0", + "@unrs/resolver-binding-linux-x64-musl": "1.11.0", + "@unrs/resolver-binding-wasm32-wasi": "1.11.0", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.0", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.0", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "license": "Apache-2.0" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/pickleglass_web/utils/api.ts b/pickleglass_web/utils/api.ts index aa80e87..958325f 100644 --- a/pickleglass_web/utils/api.ts +++ b/pickleglass_web/utils/api.ts @@ -87,7 +87,11 @@ export interface SessionDetails { const isFirebaseMode = (): boolean => { - return firebaseAuth.currentUser !== null; + // The web frontend can no longer directly access Firebase state, + // so we assume communication always goes through the backend API. + // In the future, we can create an endpoint like /api/auth/status + // in the backend to retrieve the authentication state. + return false; }; const timestampToUnix = (timestamp: Timestamp): number => { @@ -185,41 +189,13 @@ const loadRuntimeConfig = async (): Promise => { return null; }; -const getApiUrlFromElectron = (): string | null => { - if (typeof window !== 'undefined') { - try { - const { ipcRenderer } = window.require?.('electron') || {}; - if (ipcRenderer) { - try { - const apiUrl = ipcRenderer.sendSync('get-api-url-sync'); - if (apiUrl) { - console.log('βœ… API URL from Electron IPC:', apiUrl); - return apiUrl; - } - } catch (error) { - console.log('⚠️ Electron IPC failed:', error); - } - } - } catch (error) { - console.log('ℹ️ Not in Electron environment'); - } - } - return null; -}; - let apiUrlInitialized = false; let initializationPromise: Promise | null = null; const initializeApiUrl = async () => { if (apiUrlInitialized) return; - const electronUrl = getApiUrlFromElectron(); - if (electronUrl) { - API_ORIGIN = electronUrl; - apiUrlInitialized = true; - return; - } - + // Electron IPC κ΄€λ ¨ μ½”λ“œλ₯Ό λͺ¨λ‘ μ œκ±°ν•˜κ³  runtime-config.json λ˜λŠ” fallbackμ—λ§Œ μ˜μ‘΄ν•©λ‹ˆλ‹€. const runtimeUrl = await loadRuntimeConfig(); if (runtimeUrl) { API_ORIGIN = runtimeUrl; diff --git a/pickleglass_web/utils/auth.ts b/pickleglass_web/utils/auth.ts index c3de542..afac3b2 100644 --- a/pickleglass_web/utils/auth.ts +++ b/pickleglass_web/utils/auth.ts @@ -36,21 +36,12 @@ export const useAuth = () => { setUser(profile); setUserInfo(profile); - - if (window.ipcRenderer) { - window.ipcRenderer.send('set-current-user', profile.uid); - } - } else { console.log('🏠 Local mode activated'); setMode('local'); setUser(defaultLocalUser); setUserInfo(defaultLocalUser); - - if (window.ipcRenderer) { - window.ipcRenderer.send('set-current-user', defaultLocalUser.uid); - } } setIsLoading(false); }); diff --git a/preload.js b/preload.js new file mode 100644 index 0000000..e69de29 diff --git a/src/app/ApiKeyHeader.js b/src/app/ApiKeyHeader.js index b884c13..92962c8 100644 --- a/src/app/ApiKeyHeader.js +++ b/src/app/ApiKeyHeader.js @@ -1,14 +1,19 @@ -import { html, css, LitElement } from '../assets/lit-core-2.7.4.min.js'; +import { html, css, LitElement } from "../assets/lit-core-2.7.4.min.js" export class ApiKeyHeader extends LitElement { - static properties = { - apiKey: { type: String }, - isLoading: { type: Boolean }, - errorMessage: { type: String }, - selectedProvider: { type: String }, - }; + //////// after_modelStateService //////// + static properties = { + llmApiKey: { type: String }, + sttApiKey: { type: String }, + llmProvider: { type: String }, + sttProvider: { type: String }, + isLoading: { type: Boolean }, + errorMessage: { type: String }, + providers: { type: Object, state: true }, + } + //////// after_modelStateService //////// - static styles = css` + static styles = css` :host { display: block; transform: translate3d(0, 0, 0); @@ -45,7 +50,7 @@ export class ApiKeyHeader extends LitElement { } .container { - width: 285px; + width: 350px; min-height: 260px; padding: 18px 20px; background: rgba(0, 0, 0, 0.3); @@ -153,28 +158,22 @@ export class ApiKeyHeader extends LitElement { outline: none; } - .provider-select { + .providers-container { display: flex; gap: 12px; width: 100%; } + .provider-column { flex: 1; display: flex; flex-direction: column; align-items: center; } + .provider-label { color: rgba(255, 255, 255, 0.7); font-size: 11px; font-weight: 500; margin-bottom: 6px; } + .api-input, .provider-select { width: 100%; height: 34px; + text-align: center; background: rgba(255, 255, 255, 0.1); border-radius: 10px; border: 1px solid rgba(255, 255, 255, 0.2); padding: 0 10px; color: white; font-size: 12px; - font-weight: 400; margin-bottom: 6px; - text-align: center; - cursor: pointer; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2714%27%20height%3D%278%27%20viewBox%3D%270%200%2014%208%27%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%3E%3Cpath%20d%3D%27M1%201l6%206%206-6%27%20stroke%3D%27%23ffffff%27%20stroke-width%3D%271.5%27%20fill%3D%27none%27%20fill-rule%3D%27evenodd%27/%3E%3C/svg%3E'); - background-repeat: no-repeat; - background-position: right 10px center; - background-size: 12px; - padding-right: 30px; } + .provider-select option { background: #1a1a1a; color: white; } .provider-select:hover { background-color: rgba(255, 255, 255, 0.15); @@ -187,11 +186,6 @@ export class ApiKeyHeader extends LitElement { border-color: rgba(255, 255, 255, 0.4); } - .provider-select option { - background: #1a1a1a; - color: white; - padding: 5px; - } .action-button { width: 100%; @@ -239,360 +233,319 @@ export class ApiKeyHeader extends LitElement { font-weight: 500; /* Medium */ margin: 10px 0; } + - .provider-label { - color: rgba(255, 255, 255, 0.7); - font-size: 11px; - font-weight: 400; - margin-bottom: 4px; - width: 100%; - text-align: left; - } - `; - - constructor() { - super(); - this.dragState = null; - this.wasJustDragged = false; - this.apiKey = ''; - this.isLoading = false; - this.errorMessage = ''; - this.validatedApiKey = null; - this.selectedProvider = 'openai'; - - this.handleMouseMove = this.handleMouseMove.bind(this); - this.handleMouseUp = this.handleMouseUp.bind(this); - this.handleKeyPress = this.handleKeyPress.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleInput = this.handleInput.bind(this); - this.handleAnimationEnd = this.handleAnimationEnd.bind(this); - this.handleUsePicklesKey = this.handleUsePicklesKey.bind(this); - this.handleProviderChange = this.handleProviderChange.bind(this); - } - - reset() { - this.apiKey = ''; - this.isLoading = false; - this.errorMessage = ''; - this.validatedApiKey = null; - this.selectedProvider = 'openai'; - this.requestUpdate(); - } - - async handleMouseDown(e) { - if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.tagName === 'SELECT') { - return; + /* ────────────────[ GLASS BYPASS ]─────────────── */ + :host-context(body.has-glass) .container, + :host-context(body.has-glass) .api-input, + :host-context(body.has-glass) .provider-select, + :host-context(body.has-glass) .action-button, + :host-context(body.has-glass) .close-button { + background: transparent !important; + border: none !important; + box-shadow: none !important; + filter: none !important; + backdrop-filter: none !important; } - e.preventDefault(); - - const { ipcRenderer } = window.require('electron'); - const initialPosition = await ipcRenderer.invoke('get-header-position'); - - this.dragState = { - initialMouseX: e.screenX, - initialMouseY: e.screenY, - initialWindowX: initialPosition.x, - initialWindowY: initialPosition.y, - moved: false, - }; - - window.addEventListener('mousemove', this.handleMouseMove); - window.addEventListener('mouseup', this.handleMouseUp, { once: true }); - } - - handleMouseMove(e) { - if (!this.dragState) return; - - const deltaX = Math.abs(e.screenX - this.dragState.initialMouseX); - const deltaY = Math.abs(e.screenY - this.dragState.initialMouseY); - - if (deltaX > 3 || deltaY > 3) { - this.dragState.moved = true; + :host-context(body.has-glass) .container::after, + :host-context(body.has-glass) .action-button::after { + display: none !important; } - const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX); - const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY); + :host-context(body.has-glass) .action-button:hover, + :host-context(body.has-glass) .provider-select:hover, + :host-context(body.has-glass) .close-button:hover { + background: transparent !important; + } + ` - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('move-header-to', newWindowX, newWindowY); - } + constructor() { + super() + this.dragState = null + this.wasJustDragged = false + this.isLoading = false + this.errorMessage = "" + //////// after_modelStateService //////// + this.llmApiKey = ""; + this.sttApiKey = ""; + this.llmProvider = "openai"; + this.sttProvider = "openai"; + this.providers = { llm: [], stt: [] }; // μ΄ˆκΈ°ν™” + this.loadProviderConfig(); + //////// after_modelStateService //////// - handleMouseUp(e) { - if (!this.dragState) return; + this.handleMouseMove = this.handleMouseMove.bind(this) + this.handleMouseUp = this.handleMouseUp.bind(this) + this.handleKeyPress = this.handleKeyPress.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + this.handleInput = this.handleInput.bind(this) + this.handleAnimationEnd = this.handleAnimationEnd.bind(this) + this.handleUsePicklesKey = this.handleUsePicklesKey.bind(this) + this.handleProviderChange = this.handleProviderChange.bind(this) + } - const wasDragged = this.dragState.moved; + reset() { + this.apiKey = "" + this.isLoading = false + this.errorMessage = "" + this.validatedApiKey = null + this.selectedProvider = "openai" + this.requestUpdate() + } - window.removeEventListener('mousemove', this.handleMouseMove); - this.dragState = null; + async loadProviderConfig() { + if (!window.require) return; + const { ipcRenderer } = window.require('electron'); + const config = await ipcRenderer.invoke('model:get-provider-config'); + + const llmProviders = []; + const sttProviders = []; - if (wasDragged) { - this.wasJustDragged = true; - setTimeout(() => { - this.wasJustDragged = false; - }, 200); + for (const id in config) { + // 'openai-glass' 같은 가상 ProviderλŠ” UI에 ν‘œμ‹œν•˜μ§€ μ•ŠμŒ + if (id.includes('-glass')) continue; + + if (config[id].llmModels.length > 0) { + llmProviders.push({ id, name: config[id].name }); + } + if (config[id].sttModels.length > 0) { + sttProviders.push({ id, name: config[id].name }); } } - - handleInput(e) { - this.apiKey = e.target.value; - this.errorMessage = ''; - console.log('Input changed:', this.apiKey?.length || 0, 'chars'); - - this.requestUpdate(); - this.updateComplete.then(() => { - const inputField = this.shadowRoot?.querySelector('.apikey-input'); - if (inputField && this.isInputFocused) { - inputField.focus(); - } - }); - } - - handleProviderChange(e) { - this.selectedProvider = e.target.value; - this.errorMessage = ''; - console.log('Provider changed to:', this.selectedProvider); - this.requestUpdate(); - } - - handlePaste(e) { - e.preventDefault(); - this.errorMessage = ''; - const clipboardText = (e.clipboardData || window.clipboardData).getData('text'); - console.log('Paste event detected:', clipboardText?.substring(0, 10) + '...'); - - if (clipboardText) { - this.apiKey = clipboardText.trim(); - - const inputElement = e.target; - inputElement.value = this.apiKey; - } - - this.requestUpdate(); - this.updateComplete.then(() => { - const inputField = this.shadowRoot?.querySelector('.apikey-input'); - if (inputField) { - inputField.focus(); - inputField.setSelectionRange(inputField.value.length, inputField.value.length); - } - }); - } - - handleKeyPress(e) { - if (e.key === 'Enter') { - e.preventDefault(); - this.handleSubmit(); - } - } - - async handleSubmit() { - if (this.wasJustDragged || this.isLoading || !this.apiKey.trim()) { - console.log('Submit blocked:', { - wasJustDragged: this.wasJustDragged, - isLoading: this.isLoading, - hasApiKey: !!this.apiKey.trim(), - }); - return; - } - - console.log('Starting API key validation...'); - this.isLoading = true; - this.errorMessage = ''; - this.requestUpdate(); - - const apiKey = this.apiKey.trim(); - let isValid = false; - try { - const isValid = await this.validateApiKey(this.apiKey.trim(), this.selectedProvider); - - if (isValid) { - console.log('API key valid - starting slide out animation'); - this.startSlideOutAnimation(); - this.validatedApiKey = this.apiKey.trim(); - this.validatedProvider = this.selectedProvider; - } else { - this.errorMessage = 'Invalid API key - please check and try again'; - console.log('API key validation failed'); - } - } catch (error) { - console.error('API key validation error:', error); - this.errorMessage = 'Validation error - please try again'; - } finally { - this.isLoading = false; - this.requestUpdate(); - } - } - - async validateApiKey(apiKey, provider = 'openai') { - if (!apiKey || apiKey.length < 15) return false; - - if (provider === 'openai') { - if (!apiKey.match(/^[A-Za-z0-9_-]+$/)) return false; - - try { - console.log('Validating OpenAI API key...'); - - const response = await fetch('https://api.openai.com/v1/models', { - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${apiKey}`, - }, - }); - - if (response.ok) { - const data = await response.json(); - - const hasGPTModels = data.data && data.data.some(m => m.id.startsWith('gpt-')); - if (hasGPTModels) { - console.log('OpenAI API key validation successful'); - return true; - } else { - console.log('API key valid but no GPT models available'); - return false; - } - } else { - const errorData = await response.json().catch(() => ({})); - console.log('API key validation failed:', response.status, errorData.error?.message || 'Unknown error'); - return false; - } - } catch (error) { - console.error('API key validation network error:', error); - return apiKey.length >= 20; // Fallback for network issues - } - } else if (provider === 'gemini') { - // Gemini API keys typically start with 'AIza' - if (!apiKey.match(/^[A-Za-z0-9_-]+$/)) return false; - - try { - console.log('Validating Gemini API key...'); - - // Test the API key with a simple models list request - const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`); - - if (response.ok) { - const data = await response.json(); - if (data.models && data.models.length > 0) { - console.log('Gemini API key validation successful'); - return true; - } - } - - console.log('Gemini API key validation failed'); - return false; - } catch (error) { - console.error('Gemini API key validation network error:', error); - return apiKey.length >= 20; // Fallback - } - } - - return false; - } - - startSlideOutAnimation() { - this.classList.add('sliding-out'); - } - - handleUsePicklesKey(e) { - e.preventDefault(); - if (this.wasJustDragged) return; - - console.log('Requesting Firebase authentication from main process...'); - if (window.require) { - window.require('electron').ipcRenderer.invoke('start-firebase-auth'); - } - } - - handleClose() { - console.log('Close button clicked'); - if (window.require) { - window.require('electron').ipcRenderer.invoke('quit-application'); - } - } - - handleAnimationEnd(e) { - if (e.target !== this) return; - - if (this.classList.contains('sliding-out')) { - this.classList.remove('sliding-out'); - this.classList.add('hidden'); - - if (this.validatedApiKey) { - if (window.require) { - window.require('electron').ipcRenderer.invoke('api-key-validated', { - apiKey: this.validatedApiKey, - provider: this.validatedProvider || 'openai' - }); - } - this.validatedApiKey = null; - this.validatedProvider = null; - } - } - } - - connectedCallback() { - super.connectedCallback(); - this.addEventListener('animationend', this.handleAnimationEnd); - - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.removeEventListener('animationend', this.handleAnimationEnd); - - } - - render() { - const isButtonDisabled = this.isLoading || !this.apiKey || !this.apiKey.trim(); - console.log('Rendering with provider:', this.selectedProvider); - - return html` -
- -

Choose how to power your AI

- -
-
${this.errorMessage}
-
Select AI Provider:
- - (this.errorMessage = '')} - ?disabled=${this.isLoading} - autocomplete="off" - spellcheck="false" - tabindex="0" - /> - - - -
or
- - -
-
- `; - } + + this.providers = { llm: llmProviders, stt: sttProviders }; + + // κΈ°λ³Έ 선택 κ°’ μ„€μ • + if (llmProviders.length > 0) this.llmProvider = llmProviders[0].id; + if (sttProviders.length > 0) this.sttProvider = sttProviders[0].id; + + this.requestUpdate(); } -customElements.define('apikey-header', ApiKeyHeader); + async handleMouseDown(e) { + if (e.target.tagName === "INPUT" || e.target.tagName === "BUTTON" || e.target.tagName === "SELECT") { + return + } + + e.preventDefault() + + const { ipcRenderer } = window.require("electron") + const initialPosition = await ipcRenderer.invoke("get-header-position") + + this.dragState = { + initialMouseX: e.screenX, + initialMouseY: e.screenY, + initialWindowX: initialPosition.x, + initialWindowY: initialPosition.y, + moved: false, + } + + window.addEventListener("mousemove", this.handleMouseMove) + window.addEventListener("mouseup", this.handleMouseUp, { once: true }) + } + + handleMouseMove(e) { + if (!this.dragState) return + + const deltaX = Math.abs(e.screenX - this.dragState.initialMouseX) + const deltaY = Math.abs(e.screenY - this.dragState.initialMouseY) + + if (deltaX > 3 || deltaY > 3) { + this.dragState.moved = true + } + + const newWindowX = this.dragState.initialWindowX + (e.screenX - this.dragState.initialMouseX) + const newWindowY = this.dragState.initialWindowY + (e.screenY - this.dragState.initialMouseY) + + const { ipcRenderer } = window.require("electron") + ipcRenderer.invoke("move-header-to", newWindowX, newWindowY) + } + + handleMouseUp(e) { + if (!this.dragState) return + + const wasDragged = this.dragState.moved + + window.removeEventListener("mousemove", this.handleMouseMove) + this.dragState = null + + if (wasDragged) { + this.wasJustDragged = true + setTimeout(() => { + this.wasJustDragged = false + }, 200) + } + } + + handleInput(e) { + this.apiKey = e.target.value + this.errorMessage = "" + console.log("Input changed:", this.apiKey?.length || 0, "chars") + + this.requestUpdate() + this.updateComplete.then(() => { + const inputField = this.shadowRoot?.querySelector(".apikey-input") + if (inputField && this.isInputFocused) { + inputField.focus() + } + }) + } + + handleProviderChange(e) { + this.selectedProvider = e.target.value + this.errorMessage = "" + console.log("Provider changed to:", this.selectedProvider) + this.requestUpdate() + } + + handlePaste(e) { + e.preventDefault() + this.errorMessage = "" + const clipboardText = (e.clipboardData || window.clipboardData).getData("text") + console.log("Paste event detected:", clipboardText?.substring(0, 10) + "...") + + if (clipboardText) { + this.apiKey = clipboardText.trim() + + const inputElement = e.target + inputElement.value = this.apiKey + } + + this.requestUpdate() + this.updateComplete.then(() => { + const inputField = this.shadowRoot?.querySelector(".apikey-input") + if (inputField) { + inputField.focus() + inputField.setSelectionRange(inputField.value.length, inputField.value.length) + } + }) + } + + handleKeyPress(e) { + if (e.key === "Enter") { + e.preventDefault() + this.handleSubmit() + } + } + + //////// after_modelStateService //////// + async handleSubmit() { + console.log('[ApiKeyHeader] handleSubmit: Submitting API keys...'); + if (this.isLoading || !this.llmApiKey.trim() || !this.sttApiKey.trim()) { + this.errorMessage = "Please enter keys for both LLM and STT."; + return; + } + + this.isLoading = true; + this.errorMessage = ""; + this.requestUpdate(); + + const { ipcRenderer } = window.require('electron'); + + console.log('[ApiKeyHeader] handleSubmit: Validating LLM key...'); + const llmValidation = ipcRenderer.invoke('model:validate-key', { provider: this.llmProvider, key: this.llmApiKey.trim() }); + const sttValidation = ipcRenderer.invoke('model:validate-key', { provider: this.sttProvider, key: this.sttApiKey.trim() }); + + const [llmResult, sttResult] = await Promise.all([llmValidation, sttValidation]); + + if (llmResult.success && sttResult.success) { + console.log('[ApiKeyHeader] handleSubmit: Both LLM and STT keys are valid.'); + this.startSlideOutAnimation(); + } else { + console.log('[ApiKeyHeader] handleSubmit: Validation failed.'); + let errorParts = []; + if (!llmResult.success) errorParts.push(`LLM Key: ${llmResult.error || 'Invalid'}`); + if (!sttResult.success) errorParts.push(`STT Key: ${sttResult.error || 'Invalid'}`); + this.errorMessage = errorParts.join(' | '); + } + + this.isLoading = false; + this.requestUpdate(); +} +//////// after_modelStateService //////// + + + startSlideOutAnimation() { + console.log('[ApiKeyHeader] startSlideOutAnimation: Starting slide out animation.'); + this.classList.add("sliding-out") + } + + handleUsePicklesKey(e) { + e.preventDefault() + if (this.wasJustDragged) return + + console.log("Requesting Firebase authentication from main process...") + if (window.require) { + window.require("electron").ipcRenderer.invoke("start-firebase-auth") + } + } + + handleClose() { + console.log("Close button clicked") + if (window.require) { + window.require("electron").ipcRenderer.invoke("quit-application") + } + } + + + //////// after_modelStateService //////// + handleAnimationEnd(e) { + if (e.target !== this || !this.classList.contains('sliding-out')) return; + this.classList.remove("sliding-out"); + this.classList.add("hidden"); + window.require('electron').ipcRenderer.invoke('get-current-user').then(userState => { + console.log('[ApiKeyHeader] handleAnimationEnd: User state updated:', userState); + this.stateUpdateCallback?.(userState); + }); + } +//////// after_modelStateService //////// + + connectedCallback() { + super.connectedCallback() + this.addEventListener("animationend", this.handleAnimationEnd) + } + + disconnectedCallback() { + super.disconnectedCallback() + this.removeEventListener("animationend", this.handleAnimationEnd) + } + + render() { + const isButtonDisabled = this.isLoading || !this.llmApiKey.trim() || !this.sttApiKey.trim(); + + return html` +
+

Enter Your API Keys

+ +
+
+
+ + this.llmApiKey = e.target.value} ?disabled=${this.isLoading}> +
+ +
+
+ + this.sttApiKey = e.target.value} ?disabled=${this.isLoading}> +
+
+ +
${this.errorMessage}
+ + +
or
+ +
+ `; +} +} + +customElements.define("apikey-header", ApiKeyHeader) diff --git a/src/app/HeaderController.js b/src/app/HeaderController.js index 313b59e..e2c5fe8 100644 --- a/src/app/HeaderController.js +++ b/src/app/HeaderController.js @@ -1,57 +1,45 @@ -import { initializeApp } from 'firebase/app'; -import { getAuth, onAuthStateChanged, GoogleAuthProvider, signInWithCredential, signInWithCustomToken, signOut } from 'firebase/auth'; - -import './AppHeader.js'; +import './MainHeader.js'; import './ApiKeyHeader.js'; -import './PermissionSetup.js'; - -const firebaseConfig = { - apiKey: 'AIzaSyAgtJrmsFWG1C7m9S55HyT1laICEzuUS2g', - authDomain: 'pickle-3651a.firebaseapp.com', - projectId: 'pickle-3651a', - storageBucket: 'pickle-3651a.firebasestorage.app', - messagingSenderId: '904706892885', - appId: '1:904706892885:web:0e42b3dda796674ead20dc', - measurementId: 'G-SQ0WM6S28T', -}; - -const firebaseApp = initializeApp(firebaseConfig); -const auth = getAuth(firebaseApp); +import './PermissionHeader.js'; class HeaderTransitionManager { constructor() { - this.headerContainer = document.getElementById('header-container'); - this.currentHeaderType = null; // 'apikey' | 'app' | 'permission' + this.currentHeaderType = null; // 'apikey' | 'main' | 'permission' this.apiKeyHeader = null; - this.appHeader = null; - this.permissionSetup = null; + this.mainHeader = null; + this.permissionHeader = null; /** * only one header window is allowed - * @param {'apikey'|'app'|'permission'} type + * @param {'apikey'|'main'|'permission'} type */ this.ensureHeader = (type) => { - if (this.currentHeaderType === type) return; + console.log('[HeaderController] ensureHeader: Ensuring header of type:', type); + if (this.currentHeaderType === type) { + console.log('[HeaderController] ensureHeader: Header of type:', type, 'already exists.'); + return; + } this.headerContainer.innerHTML = ''; this.apiKeyHeader = null; - this.appHeader = null; - this.permissionSetup = null; + this.mainHeader = null; + this.permissionHeader = null; // Create new header element if (type === 'apikey') { this.apiKeyHeader = document.createElement('apikey-header'); + this.apiKeyHeader.stateUpdateCallback = (userState) => this.handleStateUpdate(userState); this.headerContainer.appendChild(this.apiKeyHeader); } else if (type === 'permission') { - this.permissionSetup = document.createElement('permission-setup'); - this.permissionSetup.continueCallback = () => this.transitionToAppHeader(); - this.headerContainer.appendChild(this.permissionSetup); + this.permissionHeader = document.createElement('permission-setup'); + this.permissionHeader.continueCallback = () => this.transitionToMainHeader(); + this.headerContainer.appendChild(this.permissionHeader); } else { - this.appHeader = document.createElement('app-header'); - this.headerContainer.appendChild(this.appHeader); - this.appHeader.startSlideInAnimation?.(); + this.mainHeader = document.createElement('main-header'); + this.headerContainer.appendChild(this.mainHeader); + this.mainHeader.startSlideInAnimation?.(); } this.currentHeaderType = type; @@ -60,150 +48,29 @@ class HeaderTransitionManager { console.log('[HeaderController] Manager initialized'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer - .invoke('get-current-api-key') - .then(storedKey => { - this.hasApiKey = !!storedKey; - }) - .catch(() => {}); - } - - if (window.require) { - const { ipcRenderer } = window.require('electron'); - - ipcRenderer.on('login-successful', async (event, payload) => { - const { customToken, token, error } = payload || {}; - try { - if (customToken) { - console.log('[HeaderController] Received custom token, signing in with custom token...'); - await signInWithCustomToken(auth, customToken); - return; - } - - if (token) { - console.log('[HeaderController] Received ID token, attempting Google credential sign-in...'); - const credential = GoogleAuthProvider.credential(token); - await signInWithCredential(auth, credential); - return; - } - - if (error) { - console.warn('[HeaderController] Login payload indicates verification failure. Showing permission setup.'); - // Show permission setup after login error - this.transitionToPermissionSetup(); - } - } catch (error) { - console.error('[HeaderController] Sign-in failed', error); - // Show permission setup after sign-in failure - this.transitionToPermissionSetup(); - } - }); - - - ipcRenderer.on('request-firebase-logout', async () => { - console.log('[HeaderController] Received request to sign out.'); - try { - this.hasApiKey = false; - await signOut(auth); - } catch (error) { - console.error('[HeaderController] Sign out failed', error); - } - }); - - ipcRenderer.on('api-key-validated', () => { - this.hasApiKey = true; - // Wait for animation to complete before transitioning - setTimeout(() => { - this.transitionToPermissionSetup(); - }, 350); // Give time for slide-out animation to complete - }); - - ipcRenderer.on('api-key-removed', () => { - this.hasApiKey = false; - this.transitionToApiKeyHeader(); - }); - - ipcRenderer.on('api-key-updated', () => { - this.hasApiKey = true; - if (!auth.currentUser) { - this.transitionToPermissionSetup(); - } - }); - - ipcRenderer.on('firebase-auth-success', async (event, firebaseUser) => { - console.log('[HeaderController] Received firebase-auth-success:', firebaseUser.uid); - try { - if (firebaseUser.idToken) { - const credential = GoogleAuthProvider.credential(firebaseUser.idToken); - await signInWithCredential(auth, credential); - console.log('[HeaderController] Firebase sign-in successful via ID token'); - } else { - console.warn('[HeaderController] No ID token received from deeplink, showing permission setup'); - // Show permission setup after Firebase auth - this.transitionToPermissionSetup(); - } - } catch (error) { - console.error('[HeaderController] Firebase auth failed:', error); - this.transitionToPermissionSetup(); - } - }); - } - this._bootstrap(); - onAuthStateChanged(auth, async user => { - console.log('[HeaderController] Auth state changed. User:', user ? user.email : 'null'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); + ipcRenderer.on('user-state-changed', (event, userState) => { + console.log('[HeaderController] Received user state change:', userState); + this.handleStateUpdate(userState); + }); - let userDataWithToken = null; - if (user) { - try { - const idToken = await user.getIdToken(); - userDataWithToken = { - uid: user.uid, - email: user.email, - name: user.displayName, - photoURL: user.photoURL, - idToken: idToken, - }; - } catch (error) { - console.error('[HeaderController] Failed to get ID token:', error); - userDataWithToken = { - uid: user.uid, - email: user.email, - name: user.displayName, - photoURL: user.photoURL, - idToken: null, - }; - } + ipcRenderer.on('auth-failed', (event, { message }) => { + console.error('[HeaderController] Received auth failure from main process:', message); + if (this.apiKeyHeader) { + this.apiKeyHeader.errorMessage = 'Authentication failed. Please try again.'; + this.apiKeyHeader.isLoading = false; } - - ipcRenderer.invoke('firebase-auth-state-changed', userDataWithToken).catch(console.error); - } - - if (!this.isInitialized) { - this.isInitialized = true; - return; // Skip on initial load - bootstrap handles it - } - - // Only handle state changes after initial load - if (user) { - console.log('[HeaderController] User logged in, updating hasApiKey and checking permissions...'); - this.hasApiKey = true; // User login should provide API key - // Delay permission check to ensure smooth login flow - setTimeout(() => this.transitionToPermissionSetup(), 500); - } else if (this.hasApiKey) { - console.log('[HeaderController] No Firebase user but API key exists, checking if permission setup is needed...'); - setTimeout(() => this.transitionToPermissionSetup(), 500); - } else { - console.log('[HeaderController] No auth & no API key β€” showing ApiKeyHeader'); - this.transitionToApiKeyHeader(); - } - }); + }); + ipcRenderer.on('force-show-apikey-header', async () => { + console.log('[HeaderController] Received broadcast to show apikey header. Switching now.'); + await this._resizeForApiKey(); + this.ensureHeader('apikey'); + }); + } } notifyHeaderState(stateOverride) { @@ -214,44 +81,44 @@ class HeaderTransitionManager { } async _bootstrap() { - let storedKey = null; + // The initial state will be sent by the main process via 'user-state-changed' + // We just need to request it. if (window.require) { - try { - storedKey = await window - .require('electron') - .ipcRenderer.invoke('get-current-api-key'); - } catch (_) {} - } - this.hasApiKey = !!storedKey; - - const user = await new Promise(resolve => { - const unsubscribe = onAuthStateChanged(auth, u => { - unsubscribe(); - resolve(u); - }); - }); - - // check flow order: API key -> Permissions -> App - if (!user && !this.hasApiKey) { - // No auth and no API key -> show API key input - await this._resizeForApiKey(); - this.ensureHeader('apikey'); + const userState = await window.require('electron').ipcRenderer.invoke('get-current-user'); + console.log('[HeaderController] Bootstrapping with initial user state:', userState); + this.handleStateUpdate(userState); } else { - // Has API key or user -> check permissions first - const permissionResult = await this.checkPermissions(); - if (permissionResult.success) { - // All permissions granted -> go to app - await this._resizeForApp(); - this.ensureHeader('app'); - } else { - // Permissions needed -> show permission setup - await this._resizeForPermissionSetup(); - this.ensureHeader('permission'); - } + // Fallback for non-electron environment (testing/web) + this.ensureHeader('apikey'); } } - async transitionToPermissionSetup() { + + //////// after_modelStateService //////// + async handleStateUpdate(userState) { + const { ipcRenderer } = window.require('electron'); + const isConfigured = await ipcRenderer.invoke('model:are-providers-configured'); + + if (isConfigured) { + const { isLoggedIn } = userState; + if (isLoggedIn) { + const permissionResult = await this.checkPermissions(); + if (permissionResult.success) { + this.transitionToMainHeader(); + } else { + this.transitionToPermissionHeader(); + } + } else { + this.transitionToMainHeader(); + } + } else { + await this._resizeForApiKey(); + this.ensureHeader('apikey'); + } + } + //////// after_modelStateService //////// + + async transitionToPermissionHeader() { // Prevent duplicate transitions if (this.currentHeaderType === 'permission') { console.log('[HeaderController] Already showing permission setup, skipping transition'); @@ -270,7 +137,7 @@ class HeaderTransitionManager { const permissionResult = await this.checkPermissions(); if (permissionResult.success) { // Skip permission setup if already granted - this.transitionToAppHeader(); + this.transitionToMainHeader(); return; } @@ -281,41 +148,24 @@ class HeaderTransitionManager { } } - await this._resizeForPermissionSetup(); + await this._resizeForPermissionHeader(); this.ensureHeader('permission'); } - async transitionToAppHeader(animate = true) { - if (this.currentHeaderType === 'app') { - return this._resizeForApp(); + async transitionToMainHeader(animate = true) { + if (this.currentHeaderType === 'main') { + return this._resizeForMain(); } - const canAnimate = - animate && - (this.apiKeyHeader || this.permissionSetup) && - this.currentHeaderType !== 'app'; - - if (canAnimate && this.apiKeyHeader?.startSlideOutAnimation) { - const old = this.apiKeyHeader; - const onEnd = () => { - clearTimeout(fallback); - this._resizeForApp().then(() => this.ensureHeader('app')); - }; - old.addEventListener('animationend', onEnd, { once: true }); - old.startSlideOutAnimation(); - - const fallback = setTimeout(onEnd, 450); - } else { - this.ensureHeader('app'); - this._resizeForApp(); - } + await this._resizeForMain(); + this.ensureHeader('main'); } - _resizeForApp() { + _resizeForMain() { if (!window.require) return; return window .require('electron') - .ipcRenderer.invoke('resize-header-window', { width: 353, height: 60 }) + .ipcRenderer.invoke('resize-header-window', { width: 353, height: 47 }) .catch(() => {}); } @@ -323,11 +173,11 @@ class HeaderTransitionManager { if (!window.require) return; return window .require('electron') - .ipcRenderer.invoke('resize-header-window', { width: 285, height: 300 }) + .ipcRenderer.invoke('resize-header-window', { width: 350, height: 300 }) .catch(() => {}); } - async _resizeForPermissionSetup() { + async _resizeForPermissionHeader() { if (!window.require) return; return window .require('electron') @@ -335,16 +185,6 @@ class HeaderTransitionManager { .catch(() => {}); } - async transitionToApiKeyHeader() { - await this._resizeForApiKey(); - - if (this.currentHeaderType !== 'apikey') { - this.ensureHeader('apikey'); - } - - if (this.apiKeyHeader) this.apiKeyHeader.reset(); - } - async checkPermissions() { if (!window.require) { return { success: true }; @@ -353,7 +193,6 @@ class HeaderTransitionManager { const { ipcRenderer } = window.require('electron'); try { - // Check permission status const permissions = await ipcRenderer.invoke('check-system-permissions'); console.log('[HeaderController] Current permissions:', permissions); @@ -361,7 +200,6 @@ class HeaderTransitionManager { return { success: true }; } - // If permissions are not set up, return false let errorMessage = ''; if (!permissions.microphone && !permissions.screen) { errorMessage = 'Microphone and screen recording access required'; diff --git a/src/app/AppHeader.js b/src/app/MainHeader.js similarity index 82% rename from src/app/AppHeader.js rename to src/app/MainHeader.js index faf633b..7c92f7c 100644 --- a/src/app/AppHeader.js +++ b/src/app/MainHeader.js @@ -1,13 +1,14 @@ import { html, css, LitElement } from '../assets/lit-core-2.7.4.min.js'; -export class AppHeader extends LitElement { +export class MainHeader extends LitElement { static properties = { isSessionActive: { type: Boolean, state: true }, + shortcuts: { type: Object, state: true }, }; static styles = css` :host { - display: block; + display: flex; transform: translate3d(0, 0, 0); backface-visibility: hidden; transition: transform 0.2s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.2s ease-out; @@ -99,7 +100,7 @@ export class AppHeader extends LitElement { } .header { - width: 100%; + width: max-content; height: 47px; padding: 2px 10px 2px 13px; background: transparent; @@ -212,16 +213,6 @@ export class AppHeader extends LitElement { } .action-button, - .settings-button { - background: transparent; - color: white; - border: none; - cursor: pointer; - display: flex; - align-items: center; - gap: 6px; - } - .action-text { padding-bottom: 1px; justify-content: center; @@ -275,9 +266,16 @@ export class AppHeader extends LitElement { .settings-button { padding: 5px; border-radius: 50%; + background: transparent; transition: background 0.15s ease; + color: white; + border: none; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; } - + .settings-button:hover { background: rgba(255, 255, 255, 0.1); } @@ -286,16 +284,68 @@ export class AppHeader extends LitElement { display: flex; align-items: center; justify-content: center; + padding: 3px; } .settings-icon svg { width: 16px; height: 16px; } + + /* ────────────────[ GLASS BYPASS ]─────────────── */ + :host-context(body.has-glass) .header, + :host-context(body.has-glass) .listen-button, + :host-context(body.has-glass) .header-actions, + :host-context(body.has-glass) .settings-button { + background: transparent !important; + filter: none !important; + box-shadow: none !important; + backdrop-filter: none !important; + } + :host-context(body.has-glass) .icon-box { + background: transparent !important; + border: none !important; + } + + :host-context(body.has-glass) .header::before, + :host-context(body.has-glass) .header::after, + :host-context(body.has-glass) .listen-button::before, + :host-context(body.has-glass) .listen-button::after { + display: none !important; + } + + :host-context(body.has-glass) .header-actions:hover, + :host-context(body.has-glass) .settings-button:hover, + :host-context(body.has-glass) .listen-button:hover::before { + background: transparent !important; + } + :host-context(body.has-glass) * { + animation: none !important; + transition: none !important; + transform: none !important; + filter: none !important; + backdrop-filter: none !important; + box-shadow: none !important; + } + + :host-context(body.has-glass) .header, + :host-context(body.has-glass) .listen-button, + :host-context(body.has-glass) .header-actions, + :host-context(body.has-glass) .settings-button, + :host-context(body.has-glass) .icon-box { + border-radius: 0 !important; + } + :host-context(body.has-glass) { + animation: none !important; + transition: none !important; + transform: none !important; + will-change: auto !important; + } `; constructor() { super(); + this.shortcuts = {}; this.dragState = null; this.wasJustDragged = false; this.isVisible = true; @@ -304,19 +354,6 @@ export class AppHeader extends LitElement { this.settingsHideTimer = null; this.isSessionActive = false; this.animationEndTimer = null; - - if (window.require) { - const { ipcRenderer } = window.require('electron'); - - ipcRenderer.on('toggle-header-visibility', () => { - this.toggleVisibility(); - }); - - ipcRenderer.on('cancel-hide-settings', () => { - this.cancelHideWindow('settings'); - }); - } - this.handleMouseMove = this.handleMouseMove.bind(this); this.handleMouseUp = this.handleMouseUp.bind(this); this.handleAnimationEnd = this.handleAnimationEnd.bind(this); @@ -375,7 +412,7 @@ export class AppHeader extends LitElement { toggleVisibility() { if (this.isAnimating) { - console.log('[AppHeader] Animation already in progress, ignoring toggle'); + console.log('[MainHeader] Animation already in progress, ignoring toggle'); return; } @@ -445,7 +482,7 @@ export class AppHeader extends LitElement { } else if (this.classList.contains('sliding-in')) { this.classList.remove('sliding-in'); this.hasSlidIn = true; - console.log('[AppHeader] Slide-in animation completed'); + console.log('[MainHeader] Slide-in animation completed'); } } @@ -464,6 +501,11 @@ export class AppHeader extends LitElement { this.isSessionActive = isActive; }; ipcRenderer.on('session-state-changed', this._sessionStateListener); + this._shortcutListener = (event, keybinds) => { + console.log('[MainHeader] Received updated shortcuts:', keybinds); + this.shortcuts = keybinds; + }; + ipcRenderer.on('shortcuts-updated', this._shortcutListener); } } @@ -478,11 +520,12 @@ export class AppHeader extends LitElement { if (window.require) { const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeAllListeners('toggle-header-visibility'); - ipcRenderer.removeAllListeners('cancel-hide-settings'); if (this._sessionStateListener) { ipcRenderer.removeListener('session-state-changed', this._sessionStateListener); } + if (this._shortcutListener) { + ipcRenderer.removeListener('shortcuts-updated', this._shortcutListener); + } } } @@ -499,7 +542,7 @@ export class AppHeader extends LitElement { if (this.wasJustDragged) return; if (window.require) { const { ipcRenderer } = window.require('electron'); - console.log(`[AppHeader] showWindow('${name}') called at ${Date.now()}`); + console.log(`[MainHeader] showWindow('${name}') called at ${Date.now()}`); ipcRenderer.send('cancel-hide-window', name); @@ -523,7 +566,7 @@ export class AppHeader extends LitElement { hideWindow(name) { if (this.wasJustDragged) return; if (window.require) { - console.log(`[AppHeader] hideWindow('${name}') called at ${Date.now()}`); + console.log(`[MainHeader] hideWindow('${name}') called at ${Date.now()}`); window.require('electron').ipcRenderer.send('hide-window', name); } } @@ -532,6 +575,29 @@ export class AppHeader extends LitElement { } + renderShortcut(accelerator) { + if (!accelerator) return html``; + + const keyMap = { + 'Cmd': '⌘', 'Command': '⌘', + 'Ctrl': 'βŒƒ', 'Control': 'βŒƒ', + 'Alt': 'βŒ₯', 'Option': 'βŒ₯', + 'Shift': '⇧', + 'Enter': '↡', + 'Backspace': '⌫', + 'Delete': '⌦', + 'Tab': 'β‡₯', + 'Escape': 'βŽ‹', + 'Up': '↑', 'Down': '↓', 'Left': '←', 'Right': 'β†’', + '\\': html``, + }; + + const keys = accelerator.split('+'); + return html`${keys.map(key => html` +
${keyMap[key] || key}
+ `)}`; + } + render() { return html`
@@ -564,14 +630,8 @@ export class AppHeader extends LitElement {
Ask
-
-
⌘
-
- - - - -
+
+ ${this.renderShortcut(this.shortcuts.nextStep)}
@@ -579,13 +639,8 @@ export class AppHeader extends LitElement {
Show/Hide
-
-
⌘
-
- - - -
+
+ ${this.renderShortcut(this.shortcuts.toggleVisibility)}
@@ -605,4 +660,4 @@ export class AppHeader extends LitElement { } } -customElements.define('app-header', AppHeader); +customElements.define('main-header', MainHeader); diff --git a/src/app/PermissionSetup.js b/src/app/PermissionHeader.js similarity index 88% rename from src/app/PermissionSetup.js rename to src/app/PermissionHeader.js index f8a7914..9dc9d5e 100644 --- a/src/app/PermissionSetup.js +++ b/src/app/PermissionHeader.js @@ -1,6 +1,6 @@ import { LitElement, html, css } from '../assets/lit-core-2.7.4.min.js'; -export class PermissionSetup extends LitElement { +export class PermissionHeader extends LitElement { static styles = css` :host { display: block; @@ -237,6 +237,30 @@ export class PermissionSetup extends LitElement { background: rgba(255, 255, 255, 0.2); cursor: not-allowed; } + + /* ────────────────[ GLASS BYPASS ]─────────────── */ + :host-context(body.has-glass) .container, + :host-context(body.has-glass) .action-button, + :host-context(body.has-glass) .continue-button, + :host-context(body.has-glass) .close-button { + background: transparent !important; + border: none !important; + box-shadow: none !important; + filter: none !important; + backdrop-filter: none !important; + } + + :host-context(body.has-glass) .container::after, + :host-context(body.has-glass) .action-button::after, + :host-context(body.has-glass) .continue-button::after { + display: none !important; + } + + :host-context(body.has-glass) .action-button:hover, + :host-context(body.has-glass) .continue-button:hover, + :host-context(body.has-glass) .close-button:hover { + background: transparent !important; + } `; static properties = { @@ -337,7 +361,7 @@ export class PermissionSetup extends LitElement { try { const permissions = await ipcRenderer.invoke('check-system-permissions'); - console.log('[PermissionSetup] Permission check result:', permissions); + console.log('[PermissionHeader] Permission check result:', permissions); const prevMic = this.microphoneGranted; const prevScreen = this.screenGranted; @@ -347,7 +371,7 @@ export class PermissionSetup extends LitElement { // if permissions changed == UI update if (prevMic !== this.microphoneGranted || prevScreen !== this.screenGranted) { - console.log('[PermissionSetup] Permission status changed, updating UI'); + console.log('[PermissionHeader] Permission status changed, updating UI'); this.requestUpdate(); } @@ -355,11 +379,11 @@ export class PermissionSetup extends LitElement { if (this.microphoneGranted === 'granted' && this.screenGranted === 'granted' && this.continueCallback) { - console.log('[PermissionSetup] All permissions granted, proceeding automatically'); + console.log('[PermissionHeader] All permissions granted, proceeding automatically'); setTimeout(() => this.handleContinue(), 500); } } catch (error) { - console.error('[PermissionSetup] Error checking permissions:', error); + console.error('[PermissionHeader] Error checking permissions:', error); } finally { this.isChecking = false; } @@ -368,12 +392,12 @@ export class PermissionSetup extends LitElement { async handleMicrophoneClick() { if (!window.require || this.microphoneGranted === 'granted' || this.wasJustDragged) return; - console.log('[PermissionSetup] Requesting microphone permission...'); + console.log('[PermissionHeader] Requesting microphone permission...'); const { ipcRenderer } = window.require('electron'); try { const result = await ipcRenderer.invoke('check-system-permissions'); - console.log('[PermissionSetup] Microphone permission result:', result); + console.log('[PermissionHeader] Microphone permission result:', result); if (result.microphone === 'granted') { this.microphoneGranted = 'granted'; @@ -394,19 +418,19 @@ export class PermissionSetup extends LitElement { // Check permissions again after a delay // setTimeout(() => this.checkPermissions(), 1000); } catch (error) { - console.error('[PermissionSetup] Error requesting microphone permission:', error); + console.error('[PermissionHeader] Error requesting microphone permission:', error); } } async handleScreenClick() { if (!window.require || this.screenGranted === 'granted' || this.wasJustDragged) return; - console.log('[PermissionSetup] Checking screen recording permission...'); + console.log('[PermissionHeader] Checking screen recording permission...'); const { ipcRenderer } = window.require('electron'); try { const permissions = await ipcRenderer.invoke('check-system-permissions'); - console.log('[PermissionSetup] Screen permission check result:', permissions); + console.log('[PermissionHeader] Screen permission check result:', permissions); if (permissions.screen === 'granted') { this.screenGranted = 'granted'; @@ -414,7 +438,7 @@ export class PermissionSetup extends LitElement { return; } if (permissions.screen === 'not-determined' || permissions.screen === 'denied' || permissions.screen === 'unknown' || permissions.screen === 'restricted') { - console.log('[PermissionSetup] Opening screen recording preferences...'); + console.log('[PermissionHeader] Opening screen recording preferences...'); await ipcRenderer.invoke('open-system-preferences', 'screen-recording'); } @@ -422,7 +446,7 @@ export class PermissionSetup extends LitElement { // (This may not execute if app restarts after permission grant) // setTimeout(() => this.checkPermissions(), 2000); } catch (error) { - console.error('[PermissionSetup] Error opening screen recording preferences:', error); + console.error('[PermissionHeader] Error opening screen recording preferences:', error); } } @@ -436,9 +460,9 @@ export class PermissionSetup extends LitElement { const { ipcRenderer } = window.require('electron'); try { await ipcRenderer.invoke('mark-permissions-completed'); - console.log('[PermissionSetup] Marked permissions as completed'); + console.log('[PermissionHeader] Marked permissions as completed'); } catch (error) { - console.error('[PermissionSetup] Error marking permissions as completed:', error); + console.error('[PermissionHeader] Error marking permissions as completed:', error); } } @@ -530,4 +554,4 @@ export class PermissionSetup extends LitElement { } } -customElements.define('permission-setup', PermissionSetup); \ No newline at end of file +customElements.define('permission-setup', PermissionHeader); \ No newline at end of file diff --git a/src/app/PickleGlassApp.js b/src/app/PickleGlassApp.js index a2fd887..e2e8495 100644 --- a/src/app/PickleGlassApp.js +++ b/src/app/PickleGlassApp.js @@ -1,16 +1,17 @@ import { html, css, LitElement } from '../assets/lit-core-2.7.4.min.js'; -import { CustomizeView } from '../features/customize/CustomizeView.js'; +import { SettingsView } from '../features/settings/SettingsView.js'; import { AssistantView } from '../features/listen/AssistantView.js'; -import { OnboardingView } from '../features/onboarding/OnboardingView.js'; import { AskView } from '../features/ask/AskView.js'; +import { ShortcutSettingsView } from '../features/settings/ShortCutSettingsView.js'; -import '../features/listen/renderer.js'; +import '../features/listen/renderer/renderer.js'; export class PickleGlassApp extends LitElement { static styles = css` :host { display: block; width: 100%; + height: 100%; color: var(--text-color); background: transparent; border-radius: 7px; @@ -19,11 +20,13 @@ export class PickleGlassApp extends LitElement { assistant-view { display: block; width: 100%; + height: 100%; } - ask-view, customize-view, history-view, help-view, onboarding-view, setup-view { + ask-view, settings-view, history-view, help-view, setup-view { display: block; width: 100%; + height: 100%; } `; @@ -83,10 +86,6 @@ export class PickleGlassApp extends LitElement { ipcRenderer.on('click-through-toggled', (_, isEnabled) => { this._isClickThrough = isEnabled; }); - ipcRenderer.on('show-view', (_, view) => { - this.currentView = view; - this.isMainViewVisible = true; - }); ipcRenderer.on('start-listening-session', () => { console.log('Received start-listening-session command, calling handleListenClick.'); this.handleListenClick(); @@ -100,7 +99,6 @@ export class PickleGlassApp extends LitElement { const { ipcRenderer } = window.require('electron'); ipcRenderer.removeAllListeners('update-status'); ipcRenderer.removeAllListeners('click-through-toggled'); - ipcRenderer.removeAllListeners('show-view'); ipcRenderer.removeAllListeners('start-listening-session'); } } @@ -110,10 +108,7 @@ export class PickleGlassApp extends LitElement { this.requestWindowResize(); } - if (changedProperties.has('currentView') && window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.send('view-changed', this.currentView); - + if (changedProperties.has('currentView')) { const viewContainer = this.shadowRoot?.querySelector('.view-container'); if (viewContainer) { viewContainer.classList.add('entering'); @@ -188,8 +183,8 @@ export class PickleGlassApp extends LitElement { this.isMainViewVisible = !this.isMainViewVisible; } - handleCustomizeClick() { - this.currentView = 'customize'; + handleSettingsClick() { + this.currentView = 'settings'; this.isMainViewVisible = true; } @@ -255,10 +250,6 @@ export class PickleGlassApp extends LitElement { this.currentResponseIndex = e.detail.index; } - handleOnboardingComplete() { - this.currentView = 'main'; - } - render() { switch (this.currentView) { case 'listen': @@ -271,19 +262,19 @@ export class PickleGlassApp extends LitElement { >`; case 'ask': return html``; - case 'customize': - return html` (this.selectedProfile = profile)} .onLanguageChange=${lang => (this.selectedLanguage = lang)} - >`; + >`; + case 'shortcut-settings': + return html``; case 'history': return html``; case 'help': return html``; - case 'onboarding': - return html``; case 'setup': return html``; default: diff --git a/src/app/content.html b/src/app/content.html index 48c7691..130c7ac 100644 --- a/src/app/content.html +++ b/src/app/content.html @@ -100,13 +100,13 @@ } .window-sliding-down { - animation: slideDownFromHeader 0.25s cubic-bezier(0.23, 1, 0.32, 1) forwards; + animation: slideDownFromHeader 0.12s cubic-bezier(0.23, 1, 0.32, 1) forwards; will-change: transform, opacity; transform-style: preserve-3d; } .window-sliding-up { - animation: slideUpToHeader 0.18s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; + animation: slideUpToHeader 0.10s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; will-change: transform, opacity; transform-style: preserve-3d; } @@ -156,14 +156,14 @@ } .settings-window-show { - animation: settingsPopFromButton 0.22s cubic-bezier(0.23, 1, 0.32, 1) forwards; + animation: settingsPopFromButton 0.12s cubic-bezier(0.23, 1, 0.32, 1) forwards; transform-origin: 85% 0%; will-change: transform, opacity; transform-style: preserve-3d; } .settings-window-hide { - animation: settingsCollapseToButton 0.18s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; + animation: settingsCollapseToButton 0.10s cubic-bezier(0.55, 0.085, 0.68, 0.53) forwards; transform-origin: 85% 0%; will-change: transform, opacity; transform-style: preserve-3d; @@ -250,18 +250,7 @@ if (animationTimeout) clearTimeout(animationTimeout); animationTimeout = setTimeout(() => { app.classList.remove('window-sliding-down'); - }, 250); - }); - - ipcRenderer.on('settings-window-show-animation', () => { - console.log('Starting settings window show animation'); - app.classList.remove('window-hidden', 'window-sliding-up', 'settings-window-hide'); - app.classList.add('settings-window-show'); - - if (animationTimeout) clearTimeout(animationTimeout); - animationTimeout = setTimeout(() => { - app.classList.remove('settings-window-show'); - }, 220); + }, 120); }); ipcRenderer.on('window-hide-animation', () => { @@ -273,7 +262,7 @@ animationTimeout = setTimeout(() => { app.classList.remove('window-sliding-up'); app.classList.add('window-hidden'); - }, 180); + }, 100); }); ipcRenderer.on('settings-window-hide-animation', () => { @@ -285,7 +274,7 @@ animationTimeout = setTimeout(() => { app.classList.remove('settings-window-hide'); app.classList.add('window-hidden'); - }, 180); + }, 100); }); ipcRenderer.on('listen-window-move-to-center', () => { @@ -312,5 +301,11 @@ } }); + diff --git a/src/app/header.html b/src/app/header.html index 2f35e05..46ea2d7 100644 --- a/src/app/header.html +++ b/src/app/header.html @@ -15,10 +15,14 @@
-
+ diff --git a/src/assets/aec.js b/src/assets/aec.js new file mode 100644 index 0000000..bbeec18 --- /dev/null +++ b/src/assets/aec.js @@ -0,0 +1,20 @@ +var createAecModule = (() => { + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; + return ( +async function(moduleArg = {}) { + var moduleRtn; + +var Module=moduleArg;var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof WorkerGlobalScope!="undefined";var ENVIRONMENT_IS_NODE=typeof process=="object"&&process.versions?.node&&process.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}var scriptDirectory="";var readAsync,readBinary;if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var readyPromiseResolve,readyPromiseReject;var wasmMemory;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["v"]();FS.ignorePermissions=false}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}var runDependencies=0;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return base64Decode("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AyAAQXNxIAMoAgAiASAAIAFGIgAbNgIAIABFBEAgBCACNgIIIAEhAAwDCyACED4MBQsgAyAAQXNxIAMoAgAiASAAIAFGIgIbNgIAIAEhACACRQ0BDAQLIAMgASADKAIAIgEgACABRhs2AgAgACABRyABIQANAAsDQCACKAIEIAIQPiICDQALDAILIAMgAUF+cSADKAIAIgIgASACRiIAGzYCACAADQELIAIhAQwBCwsLC2gBA38gACgCBCICKAIAIQACQANAAkAgAgJ/IABBAnFFBEAgAEERayIBQQFyQQAgARsMAQsgAEEIcUUNASAAQXZxCyACKAIAIgEgACABRiIDGzYCACABIQAgA0UNAQwCCwsgAiAAEHQLC5QCAgJ/AX4jAEEQayIDJAACf0EAIAJFDQAaA0ACQAJAAkBBAAJ/IAFB/////wcgAiACQf////8HTxsQQyIEQX9HBEAgAyAENgIMIANBBDoACEHI6QAgBEUNARogAiAESQ0CIAEgBGohASACIARrIQIMBAsgA0EAOgALIANBADsACSADQQA6AAggA0HA8wAoAgAiBDYCDCAEQRtGDQMgA0EIagspAwAiBUL/AYNCBFENBBogAC0AAEEERwRAQfj4AEEANgIAQSUgABACQfj4ACgCAEH4+ABBADYCAEEBRg0CCyAAIAU3AgBBAQwECyAEIAJBkOsAEC8ACxAAIAAgBTcCABABAAsgAg0AC0EACyADQRBqJAALgwEBA38CQCAALQAAQQNGBEAgACgCBCICKAIAIQMgAigCBCIAKAIAIgEEQEH4+ABBADYCACABIAMQAkH4+AAoAgBB+PgAQQA2AgBBAUYNAgsgACgCBARAIAAoAggaIAMQFAsgAhAUCw8LEAAgACgCBARAIAAoAggaIAMQFAsgAhAUEAEAC04BAX8gAEEAIABBmQFNG0EBdEGwwQBqLwEAQbUyaiIAECUiAkGAAU8EQCABIABB/wAQKxogAUEAOgB/QcQADwsgASAAIAJBAWoQKxpBAAtEAQF/IwBBEGsiAiQAQQIgACABIAJBDGoQBiIABH9BwPMAIAA2AgBBfwVBAAshACACKAIMIQEgAkEQaiQAQX8gASAAGwu6AQEEfyMAIgJBgCAhAyACQRBBgCAgABtrIgQkACAEIQICQAJAIABFDQAgACECIAEiAw0AQcDzAEEc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BDoAACADLQAIIgBBBEYNASAAQQNHDQEgAygCDCICKAIAIQQCQCACKAIEIgEoAgAiAARAQfj4AEEANgIAIAAgBBACQfj4ACgCAEH4+ABBADYCAEEBRg0BCyABKAIEBEAgASgCCBogBBAUCyACEBQMAgsQACEAIAEoAgQEQCABKAIIGiAEEBQLIAIQFAwDCyAAIAMpAwg3AgALIANBMGokAA8LEAAhACADLQAIQQRGDQBB+PgAQQA2AgBBJSADQQhqEAJB+PgAKAIAQfj4AEEANgIAQQFHDQAQABoQIAALIAAQAQALlgUBBn8jAEEgayIBJAACQAJAAkAgA0UNACACQQRqIQQgA0EDdCEFIANBAWtB/////wFxQQFqIQYCQANAIAQoAgANASAEQQhqIQQgB0EBaiEHIAVBCGsiBQ0ACyAGIQcLIAMgB08EQCADIAdGDQEgAyAHayEGIAIgB0EDdGohCAJAAkADQAJAIAhBgAggBiAGQYAITxsQeSIEQX9HBEAgASAENgIEIAFBBDoAACAERQRAQcjpACEEDAgLIAhBBGohByAGQQN0IQMgBkEBa0H/////AXFBAWpBACEFA0AgBCAHKAIAIglJDQIgB0EIaiEHIAVBAWohBSAEIAlrIQQgA0EIayIDDQALIQUMAQsgAUEAOgADIAFBADsAASABQQA6AAAgAUHA8wAoAgAiAjYCBCACQRtGDQEgASEEDAYLIAUgBksNASAFIAZGBEAgBEUNBSABQQA2AhggAUEBNgIMIAFCBDcCECABQeDqADYCCCABQQhqQejqABAWAAsgCCAFQQN0aiIIKAIEIgIgBEkNAiAGIAVrIQYgCCACIARrNgIEIAggCCgCACAEajYCACABLQAAIgJBBEYNACACQQNHDQAgASgCBCIDKAIAIQQCQCADKAIEIgIoAgAiBQRAQfj4AEEANgIAIAUgBBACQfj4ACgCAEH4+ABBADYCAEEBRg0BCyACKAIEBEAgAigCCBogBBAUCyADEBQMAQsLEAAgAigCBARAIAIoAggaIAQQFAsgAxAUEAEACyAFIAZB0OoAEC8ACyABQQA2AhggAUEBNgIMIAFCBDcCECABQfjqADYCCCABQQhqQYDrABAWAAsgByADQdDqABAvAAsgAEEEOgAADAELIAAgBCkDADcCAAsgAUEgaiQAC6kCAQV/IAME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IABBEGokAEHU+ABB3PcANgIAQaz4AEGAgAQ2AgBBqPgAQeD5BDYCAEGM+ABBKjYCAEGw+ABB0OIAKAIANgIACwu/aBwAQYAIC6cHwDAAAEF0dGVtcHRlZCB0byBkaXZpZGUgYnkALSsgICAwWDB4ACVzOiVkOiAlcwAvZW1zZGsvZW1zY3JpcHRlbi9zeXN0ZW0vbGliL2xpYmN4eGFiaS9zcmMvcHJpdmF0ZV90eXBlaW5mby5jcHAAdGVybWluYXRpbmcAVGhlIFZBRCBoYXMgYmVlbiByZXBsYWNlZCBieSBhIGhhY2sgcGVuZGluZyBhIGNvbXBsZXRlIHJld3JpdGUASW4tcGxhY2UgRkZUIG5vdCBzdXBwb3J0ZWQAdGVybWluYXRlX2hhbmRsZXIgdW5leHBlY3RlZGx5IHJldHVybmVkAHJ1c3RfcGFuaWMAL1VzZXJzL2hvamlueXUvcHJvamVjdHMvZW50cnkvdHdpbi9HbGFzcy9hZWMvdGFyZ2V0L3dhc20zMi11bmtub3duLWVtc2NyaXB0ZW4vcmVsZWFzZS9idWlsZC9hZWMtcnMtc3lzLTBiYWFiOTYzMzlhZjdlMDgvb3V0L3NwZWV4ZHNwL2xpYnNwZWV4ZHNwL2tpc3NfZmZ0LmMAL1VzZXJzL2hvamlueXUvcHJvamVjdHMvZW50cnkvdHdpbi9HbGFzcy9hZWMvdGFyZ2V0L3dhc20zMi11bmtub3duLWVtc2NyaXB0ZW4vcmVsZWFzZS9idWlsZC9hZWMtcnMtc3lzLTBiYWFiOTYzMzlhZjdlMDgvb3V0L3NwZWV4ZHNwL2xpYnNwZWV4ZHNwL2tpc3NfZmZ0ci5jAGNhdGNoaW5nIGEgY2xhc3Mgd2l0aG91dCBhbiBvYmplY3Q/AEtpc3NGRlQ6IG1heCByYWRpeCBzdXBwb3J0ZWQgaXMgMTcAVGhlIGVjaG8gY2FuY2VsbGVyIHN0YXJ0ZWQgYWN0aW5nIGZ1bm55IGFuZCBnb3Qgc2xhcHBlZCAocmVzZXQpLiBJdCBzd2VhcnMgaXQgd2lsbCBiZWhhdmUgbm93LgAobnVsbCkAVW5rbm93biBzcGVleF9wcmVwcm9jZXNzX2N0bCByZXF1ZXN0OiAAd2FybmluZzogJXMKAEZhdGFsIChpbnRlcm5hbCkgZXJyb3Ig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IHNsaWNlIG9mIGxlbmd0aCByYW5nZSBlbmQgaW5kZXggc2xpY2UgaW5kZXggc3RhcnRzIGF0ICBidXQgZW5kcyBhdCAAAAADAACDBCAAkQVgAF0ToAASFyAfDCBgH+8sICsqMKArb6ZgLAKo4Cwe++AtAP4gNp7/YDb9AeE2AQohNyQN4TerDmE5LxjhOTAc4UrzHuFOQDShUh5h4VPwamFUT2/hVJ28YVUAz2FWZdGhVgDaIVcA4KFYruIhWuzk4VvQ6GFcIADuXPABf10CAAAAAgAAAAcAAABjYWxsZWQgYFJlc3VsdDo6dW53cmFwKClgIG9uIGFuIGBFcnJgIHZhbHVlTGF5b3V0RXJyb3JjYXBhY2l0eSBvdmVyZmxvd2xpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMvbW9kLnJzbGlicmFyeS9hbGxvYy9zcmMvc3RyaW5nLnJzbGlicmFyeS9hbGxvYy9zcmMvZmZpL2Nfc3RyLnJzbGlicmFyeS9hbGxvYy9zcmMvc2xpY2UucnPvv71saWJyYXJ5L2FsbG9jL3NyYy9zeW5jLnJzAAC8GAAA5BcAAFN0OXR5cGVfaW5mbwAAAADkGAAAABgAANwXAABOMTBfX2N4eGFiaXYxMTZfX3NoaW1fdHlwZV9pbmZvRQAAAADkGAAAMBgAAPQXAABOMTBfX2N4eGFiaXYxMTdfX2NsYXNzX3R5cGVfaW5mb0UAAADkGAAAYBgAAPQXAABOMTBfX2N4eGFiaXYxMTdfX3BiYXNlX3R5cGVfaW5mb0UAAADkGAAAkBgAAFQYAABOMTBfX2N4eGFiaXYxMTlfX3BvaW50ZXJfdHlwZV9pbmZvRQAAAAAAJBgAABYAAAAXAAAAGAAAABkAAAAaAAAAGwAAABwAAAAdAAAAAAAAAAQZAAAWAAAAHgAAABgAAAAZAAAAGgAAAB8AAAAgAAAAIQAAAOQYAAAQGQAAJBgAAE4xMF9fY3h4YWJpdjEyMF9fc2lfY2xhc3NfdHlwZV9pbmZvRQBObyBlcnJvciBpbmZvcm1hdGlvbgBJbGxlZ2FsIGJ5dGUgc2VxdWVuY2UARG9tYWluIGVycm9yAFJlc3VsdCBub3QgcmVwcmVzZW50YWJsZQBOb3QgYSB0dHkAUGVybWlzc2lvbiBkZW5pZWQAT3BlcmF0aW9uIG5vdCBwZXJtaXR0ZWQATm8gc3VjaCBmaWxlIG9yIGRpcmVjdG9yeQBObyBzdWNoIHByb2Nlc3MARmlsZSBleGlzdHMAVmFsdWUgdG9vIGxhcmdlIGZvciBkYXRhIHR5cGUATm8gc3BhY2UgbGVmdCBvbiBkZXZpY2UAT3V0IG9mIG1lbW9yeQBSZXNvdXJjZSBidXN5AEludGVycnVwdGVkIHN5c3RlbSBjYWxsAFJlc291cmNlIHRlbXBvcmFyaWx5IHVuYXZhaWxhYmxlAEludmFsaWQgc2VlawBDcm9zcy1kZXZpY2UgbGluawBSZWFkLW9ubHkgZmlsZSBzeXN0ZW0ARGlyZWN0b3J5IG5vdCBlbXB0eQBDb25uZWN0aW9uIHJlc2V0IGJ5IHBlZXIAT3BlcmF0aW9uIHRpbWVkIG91dABDb25uZWN0aW9uIHJlZnVzZWQASG9zdCBpcyBkb3duAEhvc3QgaXMgdW5yZWFjaGFibGUAQWRkcmVzcyBpbiB1c2UAQnJva2VuIHBpcGUASS9PIGVycm9yAE5vIHN1Y2ggZGV2aWNlIG9yIGFkZHJlc3MAQmxvY2sgZGV2aWNlIHJlcXVpcmVkAE5vIHN1Y2ggZGV2aWNlAE5vdCBhIGRpcmVjdG9yeQBJcyBhIGRpcmVjdG9yeQBUZXh0IGZpbGUgYnVzeQBFeGVjIGZvcm1hdCBlcnJvcgBJbnZhbGlkIGFyZ3VtZW50AEFyZ3VtZW50IGxpc3QgdG9vIGxvbmcAU3ltYm9saWMgbGluayBsb29wAEZpbGVuYW1lIHRvbyBsb25nAFRvbyBtYW55IG9wZW4gZmlsZXMgaW4gc3lzdGVtAE5vIGZpbGUgZGVzY3JpcHRvcnMgYXZhaWxhYmxlAEJhZCBmaWxlIGRlc2NyaXB0b3IATm8gY2hpbGQgcHJvY2VzcwBCYWQgYWRkcmVzcwBGaWxlIHRvbyBsYXJnZQBUb28gbWFueSBsaW5rcwBObyBsb2NrcyBhdmFpbGFibGUAUmVzb3VyY2UgZGVhZGxvY2sgd291bGQgb2NjdXIAU3RhdGUgbm90IHJlY292ZXJhYmxlAFByZXZpb3VzIG93bmVyIGRpZWQAT3BlcmF0aW9uIGNhbmNlbGVkAEZ1bmN0aW9uIG5vdCBpbXBsZW1lbnRlZABObyBtZXNzYWdlIG9mIGRlc2lyZWQgdHlwZQBJZGVudGlmaWVyIHJlbW92ZWQARGV2aWNlIG5vdCBhIHN0cmVhbQBObyBkYXRhIGF2YWlsYWJsZQBEZXZpY2UgdGltZW91dABPdXQgb2Ygc3RyZWFtcyByZXNvdXJjZXMATGluayBoYXMgYmVlbiBzZXZlcmVkAFByb3RvY29sIGVycm9yAEJhZCBtZXNzYWdlAEZpbGUgZGVzY3JpcHRvciBpbiBiYWQgc3RhdGUATm90IGEgc29ja2V0AERlc3RpbmF0aW9uIGFkZHJlc3MgcmVxdWlyZWQATWVzc2FnZSB0b28gbGFyZ2UAUHJvdG9jb2wgd3JvbmcgdHlwZSBmb3Igc29ja2V0AFByb3RvY29sIG5vdCBhdmFpbGFibGUAUHJvdG9jb2wgbm90IHN1cHBvcnRlZABTb2NrZXQgdHlwZSBub3Qgc3VwcG9ydGVkAE5vdCBzdXBwb3J0ZWQAUHJvdG9jb2wgZmFtaWx5IG5vdCBzdXBwb3J0ZWQAQWRkcmVzcyBmYW1pbHkgbm90IHN1cHBvcnRlZCBieSBwcm90b2NvbABBZGRyZXNzIG5vdCBhdmFpbGFibGUATmV0d29yayBpcyBkb3duAE5ldHdvcmsgdW5yZWFjaGFibGUAQ29ubmVjdGlvbiByZXNldCBieSBuZXR3b3JrAENvbm5lY3Rpb24gYWJvcnRlZABObyBidWZmZXIgc3BhY2UgYXZhaWxhYmxlAFNvY2tldCBpcyBjb25uZWN0ZWQAU29ja2V0IG5vdCBjb25uZWN0ZWQAQ2Fubm90IHNlbmQgYWZ0ZXIgc29ja2V0IHNodXRkb3duAE9wZXJhdGlvbiBhbHJlYWR5IGluIHByb2dyZXNzAE9wZXJhdGlvbiBpbiBwcm9ncmVzcwBTdGFsZSBmaWxlIGhhbmRsZQBSZW1vdGUgSS9PIGVycm9yAFF1b3RhIGV4Y2VlZGVkAE5vIG1lZGl1bSBmb3VuZABXcm9uZyBtZWRpdW0gdHlwZQBNdWx0aWhvcCBhdHRlbXB0ZWQAUmVxdWlyZWQga2V5IG5vdCBhdmFpbGFibGUAS2V5IGhhcyBleHBpcmVkAEtleSBoYXMgYmVlbiByZXZva2VkAEtleSB3YXMgcmVqZWN0ZWQgYnkgc2VydmljZQAAAAClAlsA8AG1BYwFJQGDBh0DlAT/AMcDMQMLBrwBjwF/A8oEKwDaBq8AQgNOA9wBDgQVAKEGDQGUAgsCOAZkArwC/wJdA+cECwfPAssF7wXbBeECHgZFAoUAggJsA28E8QDzAxgF2QDaA0wGVAJ7AZ0DvQQAAFEAFQK7ALMDbQD/AYUELwX5BDgAZQFGAZ8AtwaoAXMCUwEAQfjCAAsMIQQAAAAAAAAAAC8CAEGYwwALBjUERwRWBABBrsMACwKgBABBwsMAC/QdRgVgBW4FYQYAAM8BAAAAAAAAAADJBukG+QYeBzkHSQdeB2xpYnJhcnkvc3RkL3NyYy9wYW5pY2tpbmcucnNyZWVudHJhbnQgaW5pdC9ydXN0Yy82YjAwYmMzODgwMTk4NjAwMTMwZTFjZjYyYjhmOGE5MzQ5NDQ4OGNjL2xpYnJhcnkvY29yZS9zcmMvY2VsbC9vbmNlLnJzY2FsbGVkIGBSZXN1bHQ6OnVud3JhcCgpYCBvbiBhbiBgRXJyYCB2YWx1ZS9ydXN0Yy82YjAwYmMzODgwMTk4NjAwMTMwZTFjZjYyYjhmOGE5MzQ5NDQ4OGNjL2xpYnJhcnkvYWxsb2Mvc3JjL3Jhd192ZWMvbW9kLnJzTnVsRXJyb3I6L3J1c3RjLzZiMDBiYzM4ODAxOTg2MDAxMzBlMWNmNjJiOGY4YTkzNDk0NDg4Y2MvbGlicmFyeS9hbGxvYy9zcmMvc2xpY2UucnN1c2Ugb2Ygc3RkOjp0aHJlYWQ6OmN1cnJlbnQoKSBpcyBub3QgcG9zc2libGUgYWZ0ZXIgdGhlIHRocmVhZCdzIGxvY2FsIGRhdGEgaGFzIGJlZW4gZGVzdHJveWVkbGlicmFyeS9zdGQvc3JjL3RocmVhZC9jdXJyZW50LnJzZmF0YWwgcnVudGltZSBlcnJvcjogCkF0dGVtcHRlZCB0byBhY2Nlc3MgdGhyZWFkLWxvY2FsIGRhdGEgd2hpbGUgYWxsb2NhdGluZyBzYWlkIGRhdGEuCkRvIG5vdCBhY2Nlc3MgZnVuY3Rpb25zIHRoYXQgYWxsb2NhdGUgaW4gdGhlIGdsb2JhbCBhbGxvY2F0b3IhClRoaXMgaXMgYSBidWcgaW4gdGhlIGdsb2JhbCBhbGxvY2F0b3IuCiwgYWJvcnRpbmcKbGlicmFyeS9zdGQvc3JjL3RocmVhZC9tb2QucnNmYWlsZWQgdG8gZ2VuZXJhdGUgdW5pcXVlIHRocmVhZCBJRDogYml0c3BhY2UgZXhoYXVzdGVkdGhyZWFkIG5hbWUgbWF5IG5vdCBjb250YWluIGludGVyaW9yIG51bGwgYnl0ZXNtYWluUlVTVF9CQUNLVFJBQ0VXb3VsZEJsb2NrAQAAAAAAAABmYWlsZWQgdG8gd3JpdGUgd2hvbGUgYnVmZmVyZW50aXR5IG5vdCBmb3VuZHBlcm1pc3Npb24gZGVuaWVkY29ubmVjdGlvbiByZWZ1c2VkY29ubmVjdGlvbiByZXNldGhvc3QgdW5yZWFjaGFibGVuZXR3b3JrIHVucmVhY2hhYmxlY29ubmVjdGlvbiBhYm9ydGVkbm90IGNvbm5lY3RlZGFkZHJlc3MgaW4gdXNlYWRkcmVzcyBub3QgYXZhaWxhYmxlbmV0d29yayBkb3duYnJva2VuIHBpcGVlbnRpdHkgYWxyZWFkeSBleGlzdHNvcGVyYXRpb24gd291bGQgYmxvY2tub3QgYSBkaXJlY3RvcnlpcyBhIGRpcmVjdG9yeWRpcmVjdG9yeSBub3QgZW1wdHlyZWFkLW9ubHkgZmlsZXN5c3RlbSBvciBzdG9yYWdlIG1lZGl1bWZpbGVzeXN0ZW0gbG9vcCBvciBpbmRpcmVjdGlvbiBsaW1pdCAoZS5nLiBzeW1saW5rIGxvb3Apc3RhbGUgbmV0d29yayBmaWxlIGhhbmRsZWludmFsaWQgaW5wdXQgcGFyYW1ldGVyaW52YWxpZCBkYXRhdGltZWQgb3V0d3JpdGUgemVyb25vIHN0b3JhZ2Ugc3BhY2VzZWVrIG9uIHVuc2Vla2FibGUgZmlsZXF1b3RhIGV4Y2VlZGVkZmlsZSB0b28gbGFyZ2VyZXNvdXJjZSBidXN5ZXhlY3V0YWJsZSBmaWxlIGJ1c3lkZWFkbG9ja2Nyb3NzLWRldmljZSBsaW5rIG9yIHJlbmFtZXRvbyBtYW55IGxpbmtzaW52YWxpZCBmaWxlbmFtZWFyZ3VtZW50IGxpc3QgdG9vIGxvbmdvcGVyYXRpb24gaW50ZXJydXB0ZWR1bnN1cHBvcnRlZHVuZXhwZWN0ZWQgZW5kIG9mIGZpbGVvdXQgb2YgbWVtb3J5aW4gcHJvZ3Jlc3NvdGhlciBlcnJvcnVuY2F0ZWdvcml6ZWQgZXJyb3JPc2NvZGVraW5kbWVzc2FnZUtpbmRFcnJvckN1c3RvbWVycm9yIChvcyBlcnJvciApbGlicmFyeS9zdGQvc3JjL2lvL21vZC5yc2EgZm9ybWF0dGluZyB0cmFpdCBpbXBsZW1lbnRhdGlvbiByZXR1cm5lZCBhbiBlcnJvciB3aGVuIHRoZSB1bmRlcmx5aW5nIHN0cmVhbSBkaWQgbm90YWR2YW5jaW5nIGlvIHNsaWNlcyBiZXlvbmQgdGhlaXIgbGVuZ3RoYWR2YW5jaW5nIElvU2xpY2UgYmV5b25kIGl0cyBsZW5ndGhsaWJyYXJ5L3N0ZC9zcmMvc3lzL2lvL2lvX3NsaWNlL2lvdmVjLnJzcGFuaWNrZWQgYXQgOgpmaWxlIG5hbWUgY29udGFpbmVkIGFuIHVuZXhwZWN0ZWQgTlVMIGJ5dGVzdGFjayBiYWNrdHJhY2U6Cm5vdGU6IFNvbWUgZGV0YWlscyBhcmUgb21pdHRlZCwgcnVuIHdpdGggYFJVU1RfQkFDS1RSQUNFPWZ1bGxgIGZvciBhIHZlcmJvc2UgYmFja3RyYWNlLgptZW1vcnkgYWxsb2NhdGlvbiBvZiAgYnl0ZXMgZmFpbGVkCiBieXRlcyBmYWlsZWRsaWJyYXJ5L3N0ZC9zcmMvYWxsb2MucnNmYXRhbCBydW50aW1lIGVycm9yOiBSdXN0IHBhbmljcyBtdXN0IGJlIHJldGhyb3duLCBhYm9ydGluZwpub3RlOiBydW4gd2l0aCBgUlVTVF9CQUNLVFJBQ0U9MWAgZW52aXJvbm1lbnQgdmFyaWFibGUgdG8gZGlzcGxheSBhIGJhY2t0cmFjZQo8dW5uYW1lZD4KdGhyZWFkICcnIHBhbmlja2VkIGF0IApCb3g8ZHluIEFueT5hYm9ydGluZyBkdWUgdG8gcGFuaWMgYXQgCnRocmVhZCBwYW5pY2tlZCB3aGlsZSBwcm9jZXNzaW5nIHBhbmljLiBhYm9ydGluZy4KdGhyZWFkIGNhdXNlZCBub24tdW53aW5kaW5nIHBhbmljLiBhYm9ydGluZy4KZmF0YWwgcnVudGltZSBlcnJvcjogZmFpbGVkIHRvIGluaXRpYXRlIHBhbmljLCBlcnJvciAsIGFib3J0aW5nCk5vdEZvdW5kUGVybWlzc2lvbkRlbmllZENvbm5lY3Rpb25SZWZ1c2VkQ29ubmVjdGlvblJlc2V0SG9zdFVucmVhY2hhYmxlTmV0d29ya1VucmVhY2hhYmxlQ29ubmVjdGlvbkFib3J0ZWROb3RDb25uZWN0ZWRBZGRySW5Vc2VBZGRyTm90QXZhaWxhYmxlTmV0d29ya0Rvd25Ccm9rZW5QaXBlQWxyZWFkeUV4aXN0c05vdEFEaXJlY3RvcnlJc0FEaXJlY3RvcnlEaXJlY3RvcnlOb3RFbXB0eVJlYWRPbmx5RmlsZXN5c3RlbUZpbGVzeXN0ZW1Mb29wU3RhbGVOZXR3b3JrRmlsZUhhbmRsZUludmFsaWRJbnB1dEludmFsaWREYXRhVGltZWRPdXRXcml0ZVplcm9TdG9yYWdlRnVsbE5vdFNlZWthYmxlUXVvdGFFeGNlZWRlZEZpbGVUb29MYXJnZVJlc291cmNlQnVzeUV4ZWN1dGFibGVGaWxlQnVzeURlYWRsb2NrQ3Jvc3Nlc0RldmljZXNUb29NYW55TGlua3NJbnZhbGlkRmlsZW5hbWVBcmd1bWVudExpc3RUb29Mb25nSW50ZXJydXB0ZWRVbnN1cHBvcnRlZFVuZXhwZWN0ZWRFb2ZPdXRPZk1lbW9yeUluUHJvZ3Jlc3NPdGhlclVuY2F0ZWdvcml6ZWRzdHJlcnJvcl9yIGZhaWx1cmVsaWJyYXJ5L3N0ZC9zcmMvc3lzL3BhbC91bml4L29zLnJzbGlicmFyeS9zdGQvc3JjL3N5cy9wYWwvdW5peC9zeW5jL2NvbmR2YXIucnMAAAAAbGlicmFyeS9zdGQvc3JjL3N5cy9wYWwvdW5peC9zeW5jL211dGV4LnJzZmFpbGVkIHRvIGxvY2sgbXV0ZXg6IAIAAABsaWJyYXJ5L3N0ZC9zcmMvc3lzL3N5bmMvcndsb2NrL3F1ZXVlLnJzZmF0YWwgcnVudGltZSBlcnJvcjogdHJpZWQgdG8gZHJvcCBub2RlIGluIGludHJ1c2l2ZSBsaXN0LiwgYWJvcnRpbmcKcGFyayBzdGF0ZSBjaGFuZ2VkIHVuZXhwZWN0ZWRseWxpYnJhcnkvc3RkL3NyYy9zeXMvc3luYy90aHJlYWRfcGFya2luZy9wdGhyZWFkLnJzaW5jb25zaXN0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")}function getBinarySync(file){if(ArrayBuffer.isView(file)){return file}if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){return{a:wasmImports}}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["u"];updateMemoryViews();wasmTable=wasmExports["H"];assignWasmExports(wasmExports);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(mod,inst)=>{resolve(receiveInstance(mod,inst))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var base64Decode=b64=>{var b1,b2,i=0,j=0,bLength=b64.length;var output=new Uint8Array((bLength*3>>2)-(b64[bLength-2]=="=")-(b64[bLength-1]=="="));for(;i>4;output[j+1]=b1<<4|b2>>2;output[j+2]=b2<<6|base64ReverseLookup[b64.charCodeAt(i+3)]}return output};var noExitRuntime=true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var exceptionLast=0;class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(sizeabort("");var abortOnCannotGrowMemory=requestedSize=>{abort("OOM")};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;abortOnCannotGrowMemory(requestedSize)};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.language||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>view=>crypto.getRandomValues(view);var randomFill=view=>{(randomFill=initRandomFill())(view)};var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var preloadPlugins=[];var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}onload?.();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url).then(processData,onerror)}else{processData(url)}};var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,readFiles:{},ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);setattr(arg,attr)},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS_modeStringToFlags(flags):flags;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}if(Module["logReadFiles"]&&!(flags&1)){if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){data=new Uint8Array(intArrayFromString(data,true))}if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAP32[buf+12>>2]=stat.uid;HEAP32[buf+16>>2]=stat.gid;HEAP32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAP32[buf+4>>2]=stats.bsize;HEAP32[buf+40>>2]=stats.bsize;HEAP32[buf+8>>2]=stats.blocks;HEAP32[buf+12>>2]=stats.bfree;HEAP32[buf+16>>2]=stats.bavail;HEAP32[buf+20>>2]=stats.files;HEAP32[buf+24>>2]=stats.ffree;HEAP32[buf+28>>2]=stats.fsid;HEAP32[buf+44>>2]=stats.flags;HEAP32[buf+36>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};for(var base64ReverseLookup=new Uint8Array(123),i=25;i>=0;--i){base64ReverseLookup[48+i]=52+i;base64ReverseLookup[65+i]=i;base64ReverseLookup[97+i]=26+i}base64ReverseLookup[43]=62;base64ReverseLookup[47]=63;FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack="";{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"]}Module["ccall"]=ccall;Module["cwrap"]=cwrap;var _AecNew,_AecCancelEcho,_AecDestroy,_malloc,_free,_setThrew,__emscripten_tempret_set,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,___cxa_can_catch;function assignWasmExports(wasmExports){Module["_AecNew"]=_AecNew=wasmExports["w"];Module["_AecCancelEcho"]=_AecCancelEcho=wasmExports["x"];Module["_AecDestroy"]=_AecDestroy=wasmExports["y"];Module["_malloc"]=_malloc=wasmExports["z"];Module["_free"]=_free=wasmExports["A"];_setThrew=wasmExports["B"];__emscripten_tempret_set=wasmExports["C"];__emscripten_stack_restore=wasmExports["D"];__emscripten_stack_alloc=wasmExports["E"];_emscripten_stack_get_current=wasmExports["F"];___cxa_can_catch=wasmExports["G"]}var wasmImports={a:___cxa_find_matching_catch_2,q:___cxa_throw,b:___resumeException,p:___syscall_getcwd,r:__abort_js,t:_emscripten_resize_heap,n:_environ_get,o:_environ_sizes_get,k:_exit,s:_fd_close,l:_fd_seek,g:_fd_write,i:invoke_ii,f:invoke_iiii,m:invoke_iiiiii,c:invoke_vi,d:invoke_vii,e:invoke_viii,j:invoke_viiii,h:invoke_viiiii};var wasmExports=await createWasm();function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}function preInit(){if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}preInit();run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} + + + return moduleRtn; +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') { + module.exports = createAecModule; + // This default export looks redundant, but it allows TS to import this + // commonjs style module. + module.exports.default = createAecModule; +} else if (typeof define === 'function' && define['amd']) + define([], () => createAecModule); diff --git a/src/common/ai/factory.js b/src/common/ai/factory.js new file mode 100644 index 0000000..ea86ff0 --- /dev/null +++ b/src/common/ai/factory.js @@ -0,0 +1,121 @@ +// factory.js + +/** + * @typedef {object} ModelOption + * @property {string} id + * @property {string} name + */ + +/** + * @typedef {object} Provider + * @property {string} name + * @property {() => any} handler + * @property {ModelOption[]} llmModels + * @property {ModelOption[]} sttModels + */ + +/** + * @type {Object.} + */ +const PROVIDERS = { + 'openai': { + name: 'OpenAI', + handler: () => require("./providers/openai"), + llmModels: [ + { id: 'gpt-4.1', name: 'GPT-4.1' }, + ], + sttModels: [ + { id: 'gpt-4o-mini-transcribe', name: 'GPT-4o Mini Transcribe' } + ], + }, + + 'openai-glass': { + name: 'OpenAI (Glass)', + handler: () => require("./providers/openai"), + llmModels: [ + { id: 'gpt-4.1-glass', name: 'GPT-4.1 (glass)' }, + ], + sttModels: [ + { id: 'gpt-4o-mini-transcribe-glass', name: 'GPT-4o Mini Transcribe (glass)' } + ], + }, + 'gemini': { + name: 'Gemini', + handler: () => require("./providers/gemini"), + llmModels: [ + { id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash' }, + ], + sttModels: [ + { id: 'gemini-live-2.5-flash-preview', name: 'Gemini Live 2.5 Flash' } + ], + }, + 'anthropic': { + name: 'Anthropic', + handler: () => require("./providers/anthropic"), + llmModels: [ + { id: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet' }, + ], + sttModels: [], + }, +}; + +function sanitizeModelId(model) { + return (typeof model === 'string') ? model.replace(/-glass$/, '') : model; +} + +function createSTT(provider, opts) { + if (provider === 'openai-glass') provider = 'openai'; + + const handler = PROVIDERS[provider]?.handler(); + if (!handler?.createSTT) { + throw new Error(`STT not supported for provider: ${provider}`); + } + if (opts && opts.model) { + opts = { ...opts, model: sanitizeModelId(opts.model) }; + } + return handler.createSTT(opts); +} + +function createLLM(provider, opts) { + if (provider === 'openai-glass') provider = 'openai'; + + const handler = PROVIDERS[provider]?.handler(); + if (!handler?.createLLM) { + throw new Error(`LLM not supported for provider: ${provider}`); + } + if (opts && opts.model) { + opts = { ...opts, model: sanitizeModelId(opts.model) }; + } + return handler.createLLM(opts); +} + +function createStreamingLLM(provider, opts) { + if (provider === 'openai-glass') provider = 'openai'; + + const handler = PROVIDERS[provider]?.handler(); + if (!handler?.createStreamingLLM) { + throw new Error(`Streaming LLM not supported for provider: ${provider}`); + } + if (opts && opts.model) { + opts = { ...opts, model: sanitizeModelId(opts.model) }; + } + return handler.createStreamingLLM(opts); +} + +function getAvailableProviders() { + const stt = []; + const llm = []; + for (const [id, provider] of Object.entries(PROVIDERS)) { + if (provider.sttModels.length > 0) stt.push(id); + if (provider.llmModels.length > 0) llm.push(id); + } + return { stt: [...new Set(stt)], llm: [...new Set(llm)] }; +} + +module.exports = { + PROVIDERS, + createSTT, + createLLM, + createStreamingLLM, + getAvailableProviders, +}; \ No newline at end of file diff --git a/src/common/ai/providers/anthropic.js b/src/common/ai/providers/anthropic.js new file mode 100644 index 0000000..f86a363 --- /dev/null +++ b/src/common/ai/providers/anthropic.js @@ -0,0 +1,292 @@ +const Anthropic = require("@anthropic-ai/sdk") + +/** + * Creates an Anthropic STT session + * Note: Anthropic doesn't have native real-time STT, so this is a placeholder + * You might want to use a different STT service or implement a workaround + * @param {object} opts - Configuration options + * @param {string} opts.apiKey - Anthropic API key + * @param {string} [opts.language='en'] - Language code + * @param {object} [opts.callbacks] - Event callbacks + * @returns {Promise} STT session placeholder + */ +async function createSTT({ apiKey, language = "en", callbacks = {}, ...config }) { + console.warn("[Anthropic] STT not natively supported. Consider using OpenAI or Gemini for STT.") + + // Return a mock STT session that doesn't actually do anything + // You might want to fallback to another provider for STT + return { + sendRealtimeInput: async (audioData) => { + console.warn("[Anthropic] STT sendRealtimeInput called but not implemented") + }, + close: async () => { + console.log("[Anthropic] STT session closed") + }, + } +} + +/** + * Creates an Anthropic LLM instance + * @param {object} opts - Configuration options + * @param {string} opts.apiKey - Anthropic API key + * @param {string} [opts.model='claude-3-5-sonnet-20241022'] - Model name + * @param {number} [opts.temperature=0.7] - Temperature + * @param {number} [opts.maxTokens=4096] - Max tokens + * @returns {object} LLM instance + */ +function createLLM({ apiKey, model = "claude-3-5-sonnet-20241022", temperature = 0.7, maxTokens = 4096, ...config }) { + const client = new Anthropic({ apiKey }) + + return { + generateContent: async (parts) => { + const messages = [] + let systemPrompt = "" + const userContent = [] + + for (const part of parts) { + if (typeof part === "string") { + if (systemPrompt === "" && part.includes("You are")) { + systemPrompt = part + } else { + userContent.push({ type: "text", text: part }) + } + } else if (part.inlineData) { + userContent.push({ + type: "image", + source: { + type: "base64", + media_type: part.inlineData.mimeType, + data: part.inlineData.data, + }, + }) + } + } + + if (userContent.length > 0) { + messages.push({ role: "user", content: userContent }) + } + + try { + const response = await client.messages.create({ + model: model, + max_tokens: maxTokens, + temperature: temperature, + system: systemPrompt || undefined, + messages: messages, + }) + + return { + response: { + text: () => response.content[0].text, + }, + raw: response, + } + } catch (error) { + console.error("Anthropic API error:", error) + throw error + } + }, + + // For compatibility with chat-style interfaces + chat: async (messages) => { + let systemPrompt = "" + const anthropicMessages = [] + + for (const msg of messages) { + if (msg.role === "system") { + systemPrompt = msg.content + } else { + // Handle multimodal content + let content + if (Array.isArray(msg.content)) { + content = [] + for (const part of msg.content) { + if (typeof part === "string") { + content.push({ type: "text", text: part }) + } else if (part.type === "text") { + content.push({ type: "text", text: part.text }) + } else if (part.type === "image_url" && part.image_url) { + // Convert base64 image to Anthropic format + const imageUrl = part.image_url.url + const [mimeInfo, base64Data] = imageUrl.split(",") + + // Extract the actual MIME type from the data URL + const mimeType = mimeInfo.match(/data:([^;]+)/)?.[1] || "image/jpeg" + + content.push({ + type: "image", + source: { + type: "base64", + media_type: mimeType, + data: base64Data, + }, + }) + } + } + } else { + content = [{ type: "text", text: msg.content }] + } + + anthropicMessages.push({ + role: msg.role === "user" ? "user" : "assistant", + content: content, + }) + } + } + + const response = await client.messages.create({ + model: model, + max_tokens: maxTokens, + temperature: temperature, + system: systemPrompt || undefined, + messages: anthropicMessages, + }) + + return { + content: response.content[0].text, + raw: response, + } + }, + } +} + +/** + * Creates an Anthropic streaming LLM instance + * @param {object} opts - Configuration options + * @param {string} opts.apiKey - Anthropic API key + * @param {string} [opts.model='claude-3-5-sonnet-20241022'] - Model name + * @param {number} [opts.temperature=0.7] - Temperature + * @param {number} [opts.maxTokens=4096] - Max tokens + * @returns {object} Streaming LLM instance + */ +function createStreamingLLM({ + apiKey, + model = "claude-3-5-sonnet-20241022", + temperature = 0.7, + maxTokens = 4096, + ...config +}) { + const client = new Anthropic({ apiKey }) + + return { + streamChat: async (messages) => { + console.log("[Anthropic Provider] Starting streaming request") + + let systemPrompt = "" + const anthropicMessages = [] + + for (const msg of messages) { + if (msg.role === "system") { + systemPrompt = msg.content + } else { + // Handle multimodal content + let content + if (Array.isArray(msg.content)) { + content = [] + for (const part of msg.content) { + if (typeof part === "string") { + content.push({ type: "text", text: part }) + } else if (part.type === "text") { + content.push({ type: "text", text: part.text }) + } else if (part.type === "image_url" && part.image_url) { + // Convert base64 image to Anthropic format + const imageUrl = part.image_url.url + const [mimeInfo, base64Data] = imageUrl.split(",") + + // Extract the actual MIME type from the data URL + const mimeType = mimeInfo.match(/data:([^;]+)/)?.[1] || "image/jpeg" + + console.log(`[Anthropic] Processing image with MIME type: ${mimeType}`) + + content.push({ + type: "image", + source: { + type: "base64", + media_type: mimeType, + data: base64Data, + }, + }) + } + } + } else { + content = [{ type: "text", text: msg.content }] + } + + anthropicMessages.push({ + role: msg.role === "user" ? "user" : "assistant", + content: content, + }) + } + } + + // Create a ReadableStream to handle Anthropic's streaming + const stream = new ReadableStream({ + async start(controller) { + try { + console.log("[Anthropic Provider] Processing messages:", anthropicMessages.length, "messages") + + let chunkCount = 0 + let totalContent = "" + + // Stream the response + const stream = await client.messages.create({ + model: model, + max_tokens: maxTokens, + temperature: temperature, + system: systemPrompt || undefined, + messages: anthropicMessages, + stream: true, + }) + + for await (const chunk of stream) { + if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") { + chunkCount++ + const chunkText = chunk.delta.text || "" + totalContent += chunkText + + // Format as SSE data + const data = JSON.stringify({ + choices: [ + { + delta: { + content: chunkText, + }, + }, + ], + }) + controller.enqueue(new TextEncoder().encode(`data: ${data}\n\n`)) + } + } + + console.log( + `[Anthropic Provider] Streamed ${chunkCount} chunks, total length: ${totalContent.length} chars`, + ) + + // Send the final done message + controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) + controller.close() + console.log("[Anthropic Provider] Streaming completed successfully") + } catch (error) { + console.error("[Anthropic Provider] Streaming error:", error) + controller.error(error) + } + }, + }) + + // Create a Response object with the stream + return new Response(stream, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }) + }, + } +} + +module.exports = { + createSTT, + createLLM, + createStreamingLLM, +} diff --git a/src/common/ai/providers/gemini.js b/src/common/ai/providers/gemini.js new file mode 100644 index 0000000..31f7e33 --- /dev/null +++ b/src/common/ai/providers/gemini.js @@ -0,0 +1,302 @@ +const { GoogleGenerativeAI } = require("@google/generative-ai") +const { GoogleGenAI } = require("@google/genai") + +/** + * Creates a Gemini STT session + * @param {object} opts - Configuration options + * @param {string} opts.apiKey - Gemini API key + * @param {string} [opts.language='en-US'] - Language code + * @param {object} [opts.callbacks] - Event callbacks + * @returns {Promise} STT session + */ +async function createSTT({ apiKey, language = "en-US", callbacks = {}, ...config }) { + const liveClient = new GoogleGenAI({ vertexai: false, apiKey }) + + // Language code BCP-47 conversion + const lang = language.includes("-") ? language : `${language}-US` + + const session = await liveClient.live.connect({ + + model: 'gemini-live-2.5-flash-preview', + callbacks: { + ...callbacks, + onMessage: (msg) => { + if (!msg || typeof msg !== 'object') return; + msg.provider = 'gemini'; + callbacks.onmessage?.(msg); + } + }, + + config: { + inputAudioTranscription: {}, + speechConfig: { languageCode: lang }, + }, + }) + + return { + sendRealtimeInput: async (payload) => session.sendRealtimeInput(payload), + close: async () => session.close(), + } +} + +/** + * Creates a Gemini LLM instance with proper text response handling + */ +function createLLM({ apiKey, model = "gemini-2.5-flash", temperature = 0.7, maxTokens = 8192, ...config }) { + const client = new GoogleGenerativeAI(apiKey) + + return { + generateContent: async (parts) => { + const geminiModel = client.getGenerativeModel({ + model: model, + generationConfig: { + temperature, + maxOutputTokens: maxTokens, + // Ensure we get text responses, not JSON + responseMimeType: "text/plain", + }, + }) + + const systemPrompt = "" + const userContent = [] + + for (const part of parts) { + if (typeof part === "string") { + // Don't automatically assume strings starting with "You are" are system prompts + // Check if it's explicitly marked as a system instruction + userContent.push(part) + } else if (part.inlineData) { + userContent.push({ + inlineData: { + mimeType: part.inlineData.mimeType, + data: part.inlineData.data, + }, + }) + } + } + + try { + const result = await geminiModel.generateContent(userContent) + const response = await result.response + + // Return plain text, not wrapped in JSON structure + return { + response: { + text: () => response.text(), + }, + } + } catch (error) { + console.error("Gemini API error:", error) + throw error + } + }, + + chat: async (messages) => { + // Filter out any system prompts that might be causing JSON responses + let systemInstruction = "" + const history = [] + let lastMessage + + messages.forEach((msg, index) => { + if (msg.role === "system") { + // Clean system instruction - avoid JSON formatting requests + systemInstruction = msg.content + .replace(/respond in json/gi, "") + .replace(/format.*json/gi, "") + .replace(/return.*json/gi, "") + + // Add explicit instruction for natural text + if (!systemInstruction.includes("respond naturally")) { + systemInstruction += "\n\nRespond naturally in plain text, not in JSON or structured format." + } + return + } + + const role = msg.role === "user" ? "user" : "model" + + if (index === messages.length - 1) { + lastMessage = msg + } else { + history.push({ role, parts: [{ text: msg.content }] }) + } + }) + + const geminiModel = client.getGenerativeModel({ + model: model, + systemInstruction: + systemInstruction || + "Respond naturally in plain text format. Do not use JSON or structured responses unless specifically requested.", + generationConfig: { + temperature: temperature, + maxOutputTokens: maxTokens, + // Force plain text responses + responseMimeType: "text/plain", + }, + }) + + const chat = geminiModel.startChat({ + history: history, + }) + + let content = lastMessage.content + + // Handle multimodal content + if (Array.isArray(content)) { + const geminiContent = [] + for (const part of content) { + if (typeof part === "string") { + geminiContent.push(part) + } else if (part.type === "text") { + geminiContent.push(part.text) + } else if (part.type === "image_url" && part.image_url) { + const base64Data = part.image_url.url.split(",")[1] + geminiContent.push({ + inlineData: { + mimeType: "image/png", + data: base64Data, + }, + }) + } + } + content = geminiContent + } + + const result = await chat.sendMessage(content) + const response = await result.response + + // Return plain text content + return { + content: response.text(), + raw: result, + } + }, + } +} + +/** + * Creates a Gemini streaming LLM instance with text response fix + */ +function createStreamingLLM({ apiKey, model = "gemini-2.5-flash", temperature = 0.7, maxTokens = 8192, ...config }) { + const client = new GoogleGenerativeAI(apiKey) + + return { + streamChat: async (messages) => { + console.log("[Gemini Provider] Starting streaming request") + + let systemInstruction = "" + const nonSystemMessages = [] + + for (const msg of messages) { + if (msg.role === "system") { + // Clean and modify system instruction + systemInstruction = msg.content + .replace(/respond in json/gi, "") + .replace(/format.*json/gi, "") + .replace(/return.*json/gi, "") + + if (!systemInstruction.includes("respond naturally")) { + systemInstruction += "\n\nRespond naturally in plain text, not in JSON or structured format." + } + } else { + nonSystemMessages.push(msg) + } + } + + const geminiModel = client.getGenerativeModel({ + model: model, + systemInstruction: + systemInstruction || + "Respond naturally in plain text format. Do not use JSON or structured responses unless specifically requested.", + generationConfig: { + temperature, + maxOutputTokens: maxTokens || 8192, + // Force plain text responses + responseMimeType: "text/plain", + }, + }) + + const stream = new ReadableStream({ + async start(controller) { + try { + const lastMessage = nonSystemMessages[nonSystemMessages.length - 1] + let geminiContent = [] + + if (Array.isArray(lastMessage.content)) { + for (const part of lastMessage.content) { + if (typeof part === "string") { + geminiContent.push(part) + } else if (part.type === "text") { + geminiContent.push(part.text) + } else if (part.type === "image_url" && part.image_url) { + const base64Data = part.image_url.url.split(",")[1] + geminiContent.push({ + inlineData: { + mimeType: "image/png", + data: base64Data, + }, + }) + } + } + } else { + geminiContent = [lastMessage.content] + } + + const contentParts = geminiContent.map((part) => { + if (typeof part === "string") { + return { text: part } + } else if (part.inlineData) { + return { inlineData: part.inlineData } + } + return part + }) + + const result = await geminiModel.generateContentStream({ + contents: [ + { + role: "user", + parts: contentParts, + }, + ], + }) + + for await (const chunk of result.stream) { + const chunkText = chunk.text() || "" + + // Format as SSE data - this should now be plain text + const data = JSON.stringify({ + choices: [ + { + delta: { + content: chunkText, + }, + }, + ], + }) + controller.enqueue(new TextEncoder().encode(`data: ${data}\n\n`)) + } + + controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n")) + controller.close() + } catch (error) { + console.error("[Gemini Provider] Streaming error:", error) + controller.error(error) + } + }, + }) + + return new Response(stream, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }) + }, + } +} + +module.exports = { + createSTT, + createLLM, + createStreamingLLM, +} diff --git a/src/common/ai/providers/openai.js b/src/common/ai/providers/openai.js new file mode 100644 index 0000000..79b1ef3 --- /dev/null +++ b/src/common/ai/providers/openai.js @@ -0,0 +1,263 @@ +const OpenAI = require('openai'); +const WebSocket = require('ws'); + +/** + * Creates an OpenAI STT session + * @param {object} opts - Configuration options + * @param {string} opts.apiKey - OpenAI API key + * @param {string} [opts.language='en'] - Language code + * @param {object} [opts.callbacks] - Event callbacks + * @param {boolean} [opts.usePortkey=false] - Whether to use Portkey + * @param {string} [opts.portkeyVirtualKey] - Portkey virtual key + * @returns {Promise} STT session + */ +async function createSTT({ apiKey, language = 'en', callbacks = {}, usePortkey = false, portkeyVirtualKey, ...config }) { + const keyType = usePortkey ? 'vKey' : 'apiKey'; + const key = usePortkey ? (portkeyVirtualKey || apiKey) : apiKey; + + const wsUrl = keyType === 'apiKey' + ? 'wss://api.openai.com/v1/realtime?intent=transcription' + : 'wss://api.portkey.ai/v1/realtime?intent=transcription'; + + const headers = keyType === 'apiKey' + ? { + 'Authorization': `Bearer ${key}`, + 'OpenAI-Beta': 'realtime=v1', + } + : { + 'x-portkey-api-key': 'gRv2UGRMq6GGLJ8aVEB4e7adIewu', + 'x-portkey-virtual-key': key, + 'OpenAI-Beta': 'realtime=v1', + }; + + const ws = new WebSocket(wsUrl, { headers }); + + return new Promise((resolve, reject) => { + ws.onopen = () => { + console.log("WebSocket session opened."); + + const sessionConfig = { + type: 'transcription_session.update', + session: { + input_audio_format: 'pcm16', + input_audio_transcription: { + model: 'gpt-4o-mini-transcribe', + prompt: config.prompt || '', + language: language || 'en' + }, + turn_detection: { + type: 'server_vad', + threshold: 0.5, + prefix_padding_ms: 200, + silence_duration_ms: 100, + }, + input_audio_noise_reduction: { + type: 'far_field' + } + } + }; + + ws.send(JSON.stringify(sessionConfig)); + + resolve({ + sendRealtimeInput: (audioData) => { + if (ws.readyState === WebSocket.OPEN) { + const message = { + type: 'input_audio_buffer.append', + audio: audioData + }; + ws.send(JSON.stringify(message)); + } + }, + close: () => { + if (ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: 'session.close' })); + ws.onmessage = ws.onerror = () => {}; // ν•Έλ“€λŸ¬ 제거 + ws.close(1000, 'Client initiated close.'); + } + } + }); + }; + + ws.onmessage = (event) => { + // ── μ’…λ£ŒΒ·ν•˜νŠΈλΉ„νŠΈ νŒ¨ν‚· 필터링 ────────────────────────────── + if (!event.data || event.data === 'null' || event.data === '[DONE]') return; + + let msg; + try { msg = JSON.parse(event.data); } + catch { return; } // JSON νŒŒμ‹± μ‹€νŒ¨ λ¬΄μ‹œ + + if (!msg || typeof msg !== 'object') return; + + msg.provider = 'openai'; // ← 항상 λͺ…μ‹œ + callbacks.onmessage?.(msg); + }; + + ws.onerror = (error) => { + console.error('WebSocket error:', error.message); + if (callbacks && callbacks.onerror) { + callbacks.onerror(error); + } + reject(error); + }; + + ws.onclose = (event) => { + console.log(`WebSocket closed: ${event.code} ${event.reason}`); + if (callbacks && callbacks.onclose) { + callbacks.onclose(event); + } + }; + }); +} + +/** + * Creates an OpenAI LLM instance + * @param {object} opts - Configuration options + * @param {string} opts.apiKey - OpenAI API key + * @param {string} [opts.model='gpt-4.1'] - Model name + * @param {number} [opts.temperature=0.7] - Temperature + * @param {number} [opts.maxTokens=2048] - Max tokens + * @param {boolean} [opts.usePortkey=false] - Whether to use Portkey + * @param {string} [opts.portkeyVirtualKey] - Portkey virtual key + * @returns {object} LLM instance + */ +function createLLM({ apiKey, model = 'gpt-4.1', temperature = 0.7, maxTokens = 2048, usePortkey = false, portkeyVirtualKey, ...config }) { + const client = new OpenAI({ apiKey }); + + const callApi = async (messages) => { + if (!usePortkey) { + const response = await client.chat.completions.create({ + model: model, + messages: messages, + temperature: temperature, + max_tokens: maxTokens + }); + return { + content: response.choices[0].message.content.trim(), + raw: response + }; + } else { + const fetchUrl = 'https://api.portkey.ai/v1/chat/completions'; + const response = await fetch(fetchUrl, { + method: 'POST', + headers: { + 'x-portkey-api-key': 'gRv2UGRMq6GGLJ8aVEB4e7adIewu', + 'x-portkey-virtual-key': portkeyVirtualKey || apiKey, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + model: model, + messages, + temperature, + max_tokens: maxTokens, + }), + }); + + if (!response.ok) { + throw new Error(`Portkey API error: ${response.status} ${response.statusText}`); + } + + const result = await response.json(); + return { + content: result.choices[0].message.content.trim(), + raw: result + }; + } + }; + + return { + generateContent: async (parts) => { + const messages = []; + let systemPrompt = ''; + let userContent = []; + + for (const part of parts) { + if (typeof part === 'string') { + if (systemPrompt === '' && part.includes('You are')) { + systemPrompt = part; + } else { + userContent.push({ type: 'text', text: part }); + } + } else if (part.inlineData) { + userContent.push({ + type: 'image_url', + image_url: { url: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}` } + }); + } + } + + if (systemPrompt) messages.push({ role: 'system', content: systemPrompt }); + if (userContent.length > 0) messages.push({ role: 'user', content: userContent }); + + const result = await callApi(messages); + + return { + response: { + text: () => result.content + }, + raw: result.raw + }; + }, + + // For compatibility with chat-style interfaces + chat: async (messages) => { + return await callApi(messages); + } + }; +} + +/** + * Creates an OpenAI streaming LLM instance + * @param {object} opts - Configuration options + * @param {string} opts.apiKey - OpenAI API key + * @param {string} [opts.model='gpt-4.1'] - Model name + * @param {number} [opts.temperature=0.7] - Temperature + * @param {number} [opts.maxTokens=2048] - Max tokens + * @param {boolean} [opts.usePortkey=false] - Whether to use Portkey + * @param {string} [opts.portkeyVirtualKey] - Portkey virtual key + * @returns {object} Streaming LLM instance + */ +function createStreamingLLM({ apiKey, model = 'gpt-4.1', temperature = 0.7, maxTokens = 2048, usePortkey = false, portkeyVirtualKey, ...config }) { + return { + streamChat: async (messages) => { + const fetchUrl = usePortkey + ? 'https://api.portkey.ai/v1/chat/completions' + : 'https://api.openai.com/v1/chat/completions'; + + const headers = usePortkey + ? { + 'x-portkey-api-key': 'gRv2UGRMq6GGLJ8aVEB4e7adIewu', + 'x-portkey-virtual-key': portkeyVirtualKey || apiKey, + 'Content-Type': 'application/json', + } + : { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }; + + const response = await fetch(fetchUrl, { + method: 'POST', + headers, + body: JSON.stringify({ + model: model, + messages, + temperature, + max_tokens: maxTokens, + stream: true, + }), + }); + + if (!response.ok) { + throw new Error(`OpenAI API error: ${response.status} ${response.statusText}`); + } + + return response; + } + }; +} + +module.exports = { + createSTT, + createLLM, + createStreamingLLM +}; \ No newline at end of file diff --git a/src/common/repositories/preset/index.js b/src/common/repositories/preset/index.js new file mode 100644 index 0000000..ebf7f77 --- /dev/null +++ b/src/common/repositories/preset/index.js @@ -0,0 +1,19 @@ +const sqliteRepository = require('./sqlite.repository'); +// const firebaseRepository = require('./firebase.repository'); +const authService = require('../../../common/services/authService'); + +function getRepository() { + // const user = authService.getCurrentUser(); + // if (user.isLoggedIn) { + // return firebaseRepository; + // } + return sqliteRepository; +} + +module.exports = { + getPresets: (...args) => getRepository().getPresets(...args), + getPresetTemplates: (...args) => getRepository().getPresetTemplates(...args), + create: (...args) => getRepository().create(...args), + update: (...args) => getRepository().update(...args), + delete: (...args) => getRepository().delete(...args), +}; \ No newline at end of file diff --git a/src/common/repositories/preset/sqlite.repository.js b/src/common/repositories/preset/sqlite.repository.js new file mode 100644 index 0000000..97ee1ca --- /dev/null +++ b/src/common/repositories/preset/sqlite.repository.js @@ -0,0 +1,85 @@ +const sqliteClient = require('../../services/sqliteClient'); + +function getPresets(uid) { + const db = sqliteClient.getDb(); + const query = ` + SELECT * FROM prompt_presets + WHERE uid = ? OR is_default = 1 + ORDER BY is_default DESC, title ASC + `; + + try { + return db.prepare(query).all(uid); + } catch (err) { + console.error('SQLite: Failed to get presets:', err); + throw err; + } +} + +function getPresetTemplates() { + const db = sqliteClient.getDb(); + const query = ` + SELECT * FROM prompt_presets + WHERE is_default = 1 + ORDER BY title ASC + `; + + try { + return db.prepare(query).all(); + } catch (err) { + console.error('SQLite: Failed to get preset templates:', err); + throw err; + } +} + +function create({ uid, title, prompt }) { + const db = sqliteClient.getDb(); + const presetId = require('crypto').randomUUID(); + const now = Math.floor(Date.now() / 1000); + const query = `INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state) VALUES (?, ?, ?, ?, 0, ?, 'dirty')`; + + try { + db.prepare(query).run(presetId, uid, title, prompt, now); + return { id: presetId }; + } catch (err) { + throw err; + } +} + +function update(id, { title, prompt }, uid) { + const db = sqliteClient.getDb(); + const query = `UPDATE prompt_presets SET title = ?, prompt = ?, sync_state = 'dirty' WHERE id = ? AND uid = ? AND is_default = 0`; + + try { + const result = db.prepare(query).run(title, prompt, id, uid); + if (result.changes === 0) { + throw new Error("Preset not found or permission denied."); + } + return { changes: result.changes }; + } catch (err) { + throw err; + } +} + +function del(id, uid) { + const db = sqliteClient.getDb(); + const query = `DELETE FROM prompt_presets WHERE id = ? AND uid = ? AND is_default = 0`; + + try { + const result = db.prepare(query).run(id, uid); + if (result.changes === 0) { + throw new Error("Preset not found or permission denied."); + } + return { changes: result.changes }; + } catch (err) { + throw err; + } +} + +module.exports = { + getPresets, + getPresetTemplates, + create, + update, + delete: del +}; \ No newline at end of file diff --git a/src/common/repositories/session/index.js b/src/common/repositories/session/index.js new file mode 100644 index 0000000..42e7ec0 --- /dev/null +++ b/src/common/repositories/session/index.js @@ -0,0 +1,26 @@ +const sqliteRepository = require('./sqlite.repository'); +// const firebaseRepository = require('./firebase.repository'); // Future implementation +const authService = require('../../../common/services/authService'); + +function getRepository() { + // In the future, we can check the user's login status from authService + // const user = authService.getCurrentUser(); + // if (user.isLoggedIn) { + // return firebaseRepository; + // } + return sqliteRepository; +} + +// Directly export functions for ease of use, decided by the strategy +module.exports = { + getById: (...args) => getRepository().getById(...args), + create: (...args) => getRepository().create(...args), + getAllByUserId: (...args) => getRepository().getAllByUserId(...args), + updateTitle: (...args) => getRepository().updateTitle(...args), + deleteWithRelatedData: (...args) => getRepository().deleteWithRelatedData(...args), + end: (...args) => getRepository().end(...args), + updateType: (...args) => getRepository().updateType(...args), + touch: (...args) => getRepository().touch(...args), + getOrCreateActive: (...args) => getRepository().getOrCreateActive(...args), + endAllActiveSessions: (...args) => getRepository().endAllActiveSessions(...args), +}; \ No newline at end of file diff --git a/src/common/repositories/session/sqlite.repository.js b/src/common/repositories/session/sqlite.repository.js new file mode 100644 index 0000000..509ba98 --- /dev/null +++ b/src/common/repositories/session/sqlite.repository.js @@ -0,0 +1,137 @@ +const sqliteClient = require('../../services/sqliteClient'); + +function getById(id) { + const db = sqliteClient.getDb(); + return db.prepare('SELECT * FROM sessions WHERE id = ?').get(id); +} + +function create(uid, type = 'ask') { + const db = sqliteClient.getDb(); + const sessionId = require('crypto').randomUUID(); + const now = Math.floor(Date.now() / 1000); + const query = `INSERT INTO sessions (id, uid, title, session_type, started_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`; + + try { + db.prepare(query).run(sessionId, uid, `Session @ ${new Date().toLocaleTimeString()}`, type, now, now); + console.log(`SQLite: Created session ${sessionId} for user ${uid} (type: ${type})`); + return sessionId; + } catch (err) { + console.error('SQLite: Failed to create session:', err); + throw err; + } +} + +function getAllByUserId(uid) { + const db = sqliteClient.getDb(); + const query = "SELECT id, uid, title, session_type, started_at, ended_at, sync_state, updated_at FROM sessions WHERE uid = ? ORDER BY started_at DESC"; + return db.prepare(query).all(uid); +} + +function updateTitle(id, title) { + const db = sqliteClient.getDb(); + const result = db.prepare('UPDATE sessions SET title = ? WHERE id = ?').run(title, id); + return { changes: result.changes }; +} + +function deleteWithRelatedData(id) { + const db = sqliteClient.getDb(); + const transaction = db.transaction(() => { + db.prepare("DELETE FROM transcripts WHERE session_id = ?").run(id); + db.prepare("DELETE FROM ai_messages WHERE session_id = ?").run(id); + db.prepare("DELETE FROM summaries WHERE session_id = ?").run(id); + db.prepare("DELETE FROM sessions WHERE id = ?").run(id); + }); + + try { + transaction(); + return { success: true }; + } catch (err) { + throw err; + } +} + +function end(id) { + const db = sqliteClient.getDb(); + const now = Math.floor(Date.now() / 1000); + const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE id = ?`; + const result = db.prepare(query).run(now, now, id); + return { changes: result.changes }; +} + +function updateType(id, type) { + const db = sqliteClient.getDb(); + const now = Math.floor(Date.now() / 1000); + const query = 'UPDATE sessions SET session_type = ?, updated_at = ? WHERE id = ?'; + const result = db.prepare(query).run(type, now, id); + return { changes: result.changes }; +} + +function touch(id) { + const db = sqliteClient.getDb(); + const now = Math.floor(Date.now() / 1000); + const query = 'UPDATE sessions SET updated_at = ? WHERE id = ?'; + const result = db.prepare(query).run(now, id); + return { changes: result.changes }; +} + +function getOrCreateActive(uid, requestedType = 'ask') { + const db = sqliteClient.getDb(); + + // 1. Look for ANY active session for the user (ended_at IS NULL). + // Prefer 'listen' sessions over 'ask' sessions to ensure continuity. + const findQuery = ` + SELECT id, session_type FROM sessions + WHERE uid = ? AND ended_at IS NULL + ORDER BY CASE session_type WHEN 'listen' THEN 1 WHEN 'ask' THEN 2 ELSE 3 END + LIMIT 1 + `; + + const activeSession = db.prepare(findQuery).get(uid); + + if (activeSession) { + // An active session exists. + console.log(`[Repo] Found active session ${activeSession.id} of type ${activeSession.session_type}`); + + // 2. Promotion Logic: If it's an 'ask' session and we need 'listen', promote it. + if (activeSession.session_type === 'ask' && requestedType === 'listen') { + updateType(activeSession.id, 'listen'); + console.log(`[Repo] Promoted session ${activeSession.id} to 'listen' type.`); + } + + // 3. Touch the session and return its ID. + touch(activeSession.id); + return activeSession.id; + } else { + // 4. No active session found, create a new one. + console.log(`[Repo] No active session for user ${uid}. Creating new '${requestedType}' session.`); + return create(uid, requestedType); + } +} + +function endAllActiveSessions() { + const db = sqliteClient.getDb(); + const now = Math.floor(Date.now() / 1000); + const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE ended_at IS NULL`; + + try { + const result = db.prepare(query).run(now, now); + console.log(`[Repo] Ended ${result.changes} active session(s).`); + return { changes: result.changes }; + } catch (err) { + console.error('SQLite: Failed to end all active sessions:', err); + throw err; + } +} + +module.exports = { + getById, + create, + getAllByUserId, + updateTitle, + deleteWithRelatedData, + end, + updateType, + touch, + getOrCreateActive, + endAllActiveSessions, +}; \ No newline at end of file diff --git a/src/common/repositories/systemSettings/index.js b/src/common/repositories/systemSettings/index.js new file mode 100644 index 0000000..aef30f1 --- /dev/null +++ b/src/common/repositories/systemSettings/index.js @@ -0,0 +1,11 @@ +const sqliteRepository = require('./sqlite.repository'); + +// This repository is not user-specific, so we always return sqlite. +function getRepository() { + return sqliteRepository; +} + +module.exports = { + markPermissionsAsCompleted: (...args) => getRepository().markPermissionsAsCompleted(...args), + checkPermissionsCompleted: (...args) => getRepository().checkPermissionsCompleted(...args), +}; \ No newline at end of file diff --git a/src/common/repositories/systemSettings/sqlite.repository.js b/src/common/repositories/systemSettings/sqlite.repository.js new file mode 100644 index 0000000..1643395 --- /dev/null +++ b/src/common/repositories/systemSettings/sqlite.repository.js @@ -0,0 +1,14 @@ +const sqliteClient = require('../../services/sqliteClient'); + +async function markPermissionsAsCompleted() { + return sqliteClient.markPermissionsAsCompleted(); +} + +async function checkPermissionsCompleted() { + return sqliteClient.checkPermissionsCompleted(); +} + +module.exports = { + markPermissionsAsCompleted, + checkPermissionsCompleted, +}; \ No newline at end of file diff --git a/src/common/repositories/user/index.js b/src/common/repositories/user/index.js new file mode 100644 index 0000000..9455ef8 --- /dev/null +++ b/src/common/repositories/user/index.js @@ -0,0 +1,19 @@ +const sqliteRepository = require('./sqlite.repository'); +// const firebaseRepository = require('./firebase.repository'); +const authService = require('../../../common/services/authService'); + +function getRepository() { + // const user = authService.getCurrentUser(); + // if (user.isLoggedIn) { + // return firebaseRepository; + // } + return sqliteRepository; +} + +module.exports = { + findOrCreate: (...args) => getRepository().findOrCreate(...args), + getById: (...args) => getRepository().getById(...args), + saveApiKey: (...args) => getRepository().saveApiKey(...args), + update: (...args) => getRepository().update(...args), + deleteById: (...args) => getRepository().deleteById(...args), +}; \ No newline at end of file diff --git a/src/common/repositories/user/sqlite.repository.js b/src/common/repositories/user/sqlite.repository.js new file mode 100644 index 0000000..7185c81 --- /dev/null +++ b/src/common/repositories/user/sqlite.repository.js @@ -0,0 +1,92 @@ +const sqliteClient = require('../../services/sqliteClient'); + +function findOrCreate(user) { + const db = sqliteClient.getDb(); + + if (!user || !user.uid) { + throw new Error('User object and uid are required'); + } + + const { uid, displayName, email } = user; + const now = Math.floor(Date.now() / 1000); + + // Validate inputs + const safeDisplayName = displayName || 'User'; + const safeEmail = email || 'no-email@example.com'; + + const query = ` + INSERT INTO users (uid, display_name, email, created_at) + VALUES (?, ?, ?, ?) + ON CONFLICT(uid) DO UPDATE SET + display_name=excluded.display_name, + email=excluded.email + `; + + try { + console.log('[SQLite] Creating/updating user:', { uid, displayName: safeDisplayName, email: safeEmail }); + db.prepare(query).run(uid, safeDisplayName, safeEmail, now); + const result = getById(uid); + console.log('[SQLite] User operation successful:', result); + return result; + } catch (err) { + console.error('SQLite: Failed to find or create user:', err); + console.error('SQLite: User data:', { uid, displayName: safeDisplayName, email: safeEmail }); + throw new Error(`Failed to create user in database: ${err.message}`); + } +} + +function getById(uid) { + const db = sqliteClient.getDb(); + return db.prepare('SELECT * FROM users WHERE uid = ?').get(uid); +} + +function saveApiKey(apiKey, uid, provider = 'openai') { + const db = sqliteClient.getDb(); + try { + const result = db.prepare('UPDATE users SET api_key = ?, provider = ? WHERE uid = ?').run(apiKey, provider, uid); + console.log(`SQLite: API key saved for user ${uid} with provider ${provider}.`); + return { changes: result.changes }; + } catch (err) { + console.error('SQLite: Failed to save API key:', err); + throw err; + } +} + +function update({ uid, displayName }) { + const db = sqliteClient.getDb(); + const result = db.prepare('UPDATE users SET display_name = ? WHERE uid = ?').run(displayName, uid); + return { changes: result.changes }; +} + +function deleteById(uid) { + const db = sqliteClient.getDb(); + const userSessions = db.prepare('SELECT id FROM sessions WHERE uid = ?').all(uid); + const sessionIds = userSessions.map(s => s.id); + + const transaction = db.transaction(() => { + if (sessionIds.length > 0) { + const placeholders = sessionIds.map(() => '?').join(','); + db.prepare(`DELETE FROM transcripts WHERE session_id IN (${placeholders})`).run(...sessionIds); + db.prepare(`DELETE FROM ai_messages WHERE session_id IN (${placeholders})`).run(...sessionIds); + db.prepare(`DELETE FROM summaries WHERE session_id IN (${placeholders})`).run(...sessionIds); + db.prepare(`DELETE FROM sessions WHERE uid = ?`).run(uid); + } + db.prepare('DELETE FROM prompt_presets WHERE uid = ? AND is_default = 0').run(uid); + db.prepare('DELETE FROM users WHERE uid = ?').run(uid); + }); + + try { + transaction(); + return { success: true }; + } catch (err) { + throw err; + } +} + +module.exports = { + findOrCreate, + getById, + saveApiKey, + update, + deleteById +}; \ No newline at end of file diff --git a/src/common/services/aiProviderService.js b/src/common/services/aiProviderService.js deleted file mode 100644 index fe24e47..0000000 --- a/src/common/services/aiProviderService.js +++ /dev/null @@ -1,377 +0,0 @@ -const { createOpenAiGenerativeClient, getOpenAiGenerativeModel } = require('./openAiClient.js'); -const { createGeminiClient, getGeminiGenerativeModel, createGeminiChat } = require('./googleGeminiClient.js'); - -/** - * Creates an AI client based on the provider - * @param {string} apiKey - The API key - * @param {string} provider - The provider ('openai' or 'gemini') - * @returns {object} The AI client - */ -function createAIClient(apiKey, provider = 'openai') { - switch (provider) { - case 'openai': - return createOpenAiGenerativeClient(apiKey); - case 'gemini': - return createGeminiClient(apiKey); - default: - throw new Error(`Unsupported AI provider: ${provider}`); - } -} - -/** - * Gets a generative model based on the provider - * @param {object} client - The AI client - * @param {string} provider - The provider ('openai' or 'gemini') - * @param {string} model - The model name (optional) - * @returns {object} The model object - */ -function getGenerativeModel(client, provider = 'openai', model) { - switch (provider) { - case 'openai': - return getOpenAiGenerativeModel(client, model || 'gpt-4.1'); - case 'gemini': - return getGeminiGenerativeModel(client, model || 'gemini-2.5-flash'); - default: - throw new Error(`Unsupported AI provider: ${provider}`); - } -} - -/** - * Makes a chat completion request based on the provider - * @param {object} params - Request parameters - * @returns {Promise} The completion response - */ -async function makeChatCompletion({ apiKey, provider = 'openai', messages, temperature = 0.7, maxTokens = 1024, model, stream = false }) { - if (provider === 'openai') { - const fetchUrl = 'https://api.openai.com/v1/chat/completions'; - const response = await fetch(fetchUrl, { - method: 'POST', - headers: { - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - model: model || 'gpt-4.1', - messages, - temperature, - max_tokens: maxTokens, - stream, - }), - }); - - if (!response.ok) { - throw new Error(`OpenAI API error: ${response.status} ${response.statusText}`); - } - - if (stream) { - return response; - } - - const result = await response.json(); - return { - content: result.choices[0].message.content.trim(), - raw: result - }; - } else if (provider === 'gemini') { - const client = createGeminiClient(apiKey); - const genModel = getGeminiGenerativeModel(client, model || 'gemini-2.5-flash'); - - // Convert OpenAI format messages to Gemini format - const parts = []; - for (const message of messages) { - if (message.role === 'system') { - parts.push(message.content); - } else if (message.role === 'user') { - if (typeof message.content === 'string') { - parts.push(message.content); - } else if (Array.isArray(message.content)) { - // Handle multimodal content - for (const part of message.content) { - if (part.type === 'text') { - parts.push(part.text); - } else if (part.type === 'image_url' && part.image_url?.url) { - // Extract base64 data from data URL - const base64Match = part.image_url.url.match(/^data:(.+);base64,(.+)$/); - if (base64Match) { - parts.push({ - inlineData: { - mimeType: base64Match[1], - data: base64Match[2] - } - }); - } - } - } - } - } - } - - const result = await genModel.generateContent(parts); - return { - content: result.response.text(), - raw: result - }; - } else { - throw new Error(`Unsupported AI provider: ${provider}`); - } -} - -/** - * Makes a chat completion request with Portkey support - * @param {object} params - Request parameters including Portkey options - * @returns {Promise} The completion response - */ -async function makeChatCompletionWithPortkey({ - apiKey, - provider = 'openai', - messages, - temperature = 0.7, - maxTokens = 1024, - model, - usePortkey = false, - portkeyVirtualKey = null -}) { - if (!usePortkey) { - return makeChatCompletion({ apiKey, provider, messages, temperature, maxTokens, model }); - } - - // Portkey is only supported for OpenAI currently - if (provider !== 'openai') { - console.warn('Portkey is only supported for OpenAI provider, falling back to direct API'); - return makeChatCompletion({ apiKey, provider, messages, temperature, maxTokens, model }); - } - - const fetchUrl = 'https://api.portkey.ai/v1/chat/completions'; - const response = await fetch(fetchUrl, { - method: 'POST', - headers: { - 'x-portkey-api-key': 'gRv2UGRMq6GGLJ8aVEB4e7adIewu', - 'x-portkey-virtual-key': portkeyVirtualKey || apiKey, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - model: model || 'gpt-4.1', - messages, - temperature, - max_tokens: maxTokens, - }), - }); - - if (!response.ok) { - throw new Error(`Portkey API error: ${response.status} ${response.statusText}`); - } - - const result = await response.json(); - return { - content: result.choices[0].message.content.trim(), - raw: result - }; -} - -/** - * Makes a streaming chat completion request - * @param {object} params - Request parameters - * @returns {Promise} The streaming response - */ -async function makeStreamingChatCompletion({ apiKey, provider = 'openai', messages, temperature = 0.7, maxTokens = 1024, model }) { - if (provider === 'openai') { - const fetchUrl = 'https://api.openai.com/v1/chat/completions'; - const response = await fetch(fetchUrl, { - method: 'POST', - headers: { - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - model: model || 'gpt-4.1', - messages, - temperature, - max_tokens: maxTokens, - stream: true, - }), - }); - - if (!response.ok) { - throw new Error(`OpenAI API error: ${response.status} ${response.statusText}`); - } - - return response; - } else if (provider === 'gemini') { - console.log('[AIProviderService] Starting Gemini streaming request'); - // Gemini streaming requires a different approach - // We'll create a ReadableStream that mimics OpenAI's SSE format - const geminiClient = createGeminiClient(apiKey); - - // Extract system instruction if present - let systemInstruction = ''; - const nonSystemMessages = []; - - for (const msg of messages) { - if (msg.role === 'system') { - systemInstruction = msg.content; - } else { - nonSystemMessages.push(msg); - } - } - - const chat = createGeminiChat(geminiClient, model || 'gemini-2.0-flash-exp', { - temperature, - maxOutputTokens: maxTokens || 8192, - systemInstruction: systemInstruction || undefined - }); - - // Create a ReadableStream to handle Gemini's streaming - const stream = new ReadableStream({ - async start(controller) { - try { - console.log('[AIProviderService] Processing messages for Gemini:', nonSystemMessages.length, 'messages (excluding system)'); - - // Get the last user message - const lastMessage = nonSystemMessages[nonSystemMessages.length - 1]; - let lastUserMessage = lastMessage.content; - - // Handle case where content might be an array (multimodal) - if (Array.isArray(lastUserMessage)) { - // Extract text content from array - const textParts = lastUserMessage.filter(part => - typeof part === 'string' || (part && part.type === 'text') - ); - lastUserMessage = textParts.map(part => - typeof part === 'string' ? part : part.text - ).join(' '); - } - - console.log('[AIProviderService] Sending message to Gemini:', - typeof lastUserMessage === 'string' ? lastUserMessage.substring(0, 100) + '...' : 'multimodal content'); - - // Prepare the message content for Gemini - let geminiContent = []; - - // Handle multimodal content properly - if (Array.isArray(lastMessage.content)) { - for (const part of lastMessage.content) { - if (typeof part === 'string') { - geminiContent.push(part); - } else if (part.type === 'text') { - geminiContent.push(part.text); - } else if (part.type === 'image_url' && part.image_url) { - // Convert base64 image to Gemini format - const base64Data = part.image_url.url.split(',')[1]; - geminiContent.push({ - inlineData: { - mimeType: 'image/png', - data: base64Data - } - }); - } - } - } else { - geminiContent = [lastUserMessage]; - } - - console.log('[AIProviderService] Prepared Gemini content:', - geminiContent.length, 'parts'); - - // Stream the response - let chunkCount = 0; - let totalContent = ''; - - for await (const chunk of chat.sendMessageStream(geminiContent)) { - chunkCount++; - const chunkText = chunk.text || ''; - totalContent += chunkText; - - // Format as SSE data - const data = JSON.stringify({ - choices: [{ - delta: { - content: chunkText - } - }] - }); - controller.enqueue(new TextEncoder().encode(`data: ${data}\n\n`)); - } - - console.log(`[AIProviderService] Streamed ${chunkCount} chunks, total length: ${totalContent.length} chars`); - - // Send the final done message - controller.enqueue(new TextEncoder().encode('data: [DONE]\n\n')); - controller.close(); - console.log('[AIProviderService] Gemini streaming completed successfully'); - } catch (error) { - console.error('[AIProviderService] Gemini streaming error:', error); - controller.error(error); - } - } - }); - - // Create a Response object with the stream - return new Response(stream, { - headers: { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive' - } - }); - } else { - throw new Error(`Unsupported AI provider: ${provider}`); - } -} - -/** - * Makes a streaming chat completion request with Portkey support - * @param {object} params - Request parameters - * @returns {Promise} The streaming response - */ -async function makeStreamingChatCompletionWithPortkey({ - apiKey, - provider = 'openai', - messages, - temperature = 0.7, - maxTokens = 1024, - model, - usePortkey = false, - portkeyVirtualKey = null -}) { - if (!usePortkey) { - return makeStreamingChatCompletion({ apiKey, provider, messages, temperature, maxTokens, model }); - } - - // Portkey is only supported for OpenAI currently - if (provider !== 'openai') { - console.warn('Portkey is only supported for OpenAI provider, falling back to direct API'); - return makeStreamingChatCompletion({ apiKey, provider, messages, temperature, maxTokens, model }); - } - - const fetchUrl = 'https://api.portkey.ai/v1/chat/completions'; - const response = await fetch(fetchUrl, { - method: 'POST', - headers: { - 'x-portkey-api-key': 'gRv2UGRMq6GGLJ8aVEB4e7adIewu', - 'x-portkey-virtual-key': portkeyVirtualKey || apiKey, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - model: model || 'gpt-4.1', - messages, - temperature, - max_tokens: maxTokens, - stream: true, - }), - }); - - if (!response.ok) { - throw new Error(`Portkey API error: ${response.status} ${response.statusText}`); - } - - return response; -} - -module.exports = { - createAIClient, - getGenerativeModel, - makeChatCompletion, - makeChatCompletionWithPortkey, - makeStreamingChatCompletion, - makeStreamingChatCompletionWithPortkey -}; \ No newline at end of file diff --git a/src/common/services/apiClient.js b/src/common/services/apiClient.js deleted file mode 100644 index 7f27e6a..0000000 --- a/src/common/services/apiClient.js +++ /dev/null @@ -1,239 +0,0 @@ -const axios = require('axios'); -const config = require('../config/config'); - -class APIClient { - constructor() { - this.baseURL = config.get('apiUrl'); - this.client = axios.create({ - baseURL: this.baseURL, - timeout: config.get('apiTimeout'), - headers: { - 'Content-Type': 'application/json' - } - }); - - this.client.interceptors.response.use( - (response) => response, - (error) => { - console.error('API request failed:', error.message); - if (error.response) { - console.error('response status:', error.response.status); - console.error('response data:', error.response.data); - } - return Promise.reject(error); - } - ); - } - - async initialize() { - try { - const response = await this.client.get('/api/auth/status'); - console.log('[APIClient] checked default user status:', response.data); - return true; - } catch (error) { - console.error('[APIClient] failed to initialize:', error); - return false; - } - } - - async checkConnection() { - try { - const response = await this.client.get('/'); - return response.status === 200; - } catch (error) { - return false; - } - } - - async saveApiKey(apiKey) { - try { - const response = await this.client.post('/api/user/api-key', { apiKey }); - return response.data; - } catch (error) { - console.error('failed to save api key:', error); - throw error; - } - } - - async checkApiKey() { - try { - const response = await this.client.get('/api/user/api-key'); - return response.data; - } catch (error) { - console.error('failed to check api key:', error); - return { hasApiKey: false }; - } - } - - async getUserBatchData(includes = ['profile', 'context', 'presets']) { - try { - const includeParam = includes.join(','); - const response = await this.client.get(`/api/user/batch?include=${includeParam}`); - return response.data; - } catch (error) { - console.error('failed to get user batch data:', error); - return null; - } - } - - async getUserContext() { - try { - const response = await this.client.get('/api/user/context'); - return response.data.context; - } catch (error) { - console.error('fail to get user context:', error); - return null; - } - } - - async getUserProfile() { - try { - const response = await this.client.get('/api/user/profile'); - return response.data; - } catch (error) { - console.error('failed to get user profile:', error); - return null; - } - } - - async getUserPresets() { - try { - const response = await this.client.get('/api/user/presets'); - return response.data; - } catch (error) { - console.error('failed to get user presets:', error); - return []; - } - } - - async updateUserContext(context) { - try { - const response = await this.client.post('/api/user/context', context); - return response.data; - } catch (error) { - console.error('failed to update user context:', error); - throw error; - } - } - - async addActivity(activity) { - try { - const response = await this.client.post('/api/user/activities', activity); - return response.data; - } catch (error) { - console.error('failed to add activity:', error); - throw error; - } - } - - async getPresetTemplates() { - try { - const response = await this.client.get('/api/preset-templates'); - return response.data; - } catch (error) { - console.error('failed to get preset templates:', error); - return []; - } - } - - async updateUserProfile(profile) { - try { - const response = await this.client.post('/api/user/profile', profile); - return response.data; - } catch (error) { - console.error('failed to update user profile:', error); - throw error; - } - } - - async searchUsers(name = '') { - try { - const response = await this.client.get('/api/users/search', { - params: { name } - }); - return response.data; - } catch (error) { - console.error('failed to search users:', error); - return []; - } - } - - async getUserProfileById(userId) { - try { - const response = await this.client.get(`/api/users/${userId}/profile`); - return response.data; - } catch (error) { - console.error('failed to get user profile by id:', error); - return null; - } - } - - async saveConversationSession(sessionId, conversationHistory) { - try { - const payload = { - sessionId, - conversationHistory - }; - const response = await this.client.post('/api/conversations', payload); - return response.data; - } catch (error) { - console.error('failed to save conversation session:', error); - throw error; - } - } - - async getConversationSession(sessionId) { - try { - const response = await this.client.get(`/api/conversations/${sessionId}`); - return response.data; - } catch (error) { - console.error('failed to get conversation session:', error); - return null; - } - } - - async getAllConversationSessions() { - try { - const response = await this.client.get('/api/conversations'); - return response.data; - } catch (error) { - console.error('failed to get all conversation sessions:', error); - return []; - } - } - - async deleteConversationSession(sessionId) { - try { - const response = await this.client.delete(`/api/conversations/${sessionId}`); - return response.data; - } catch (error) { - console.error('failed to delete conversation session:', error); - throw error; - } - } - - async getSyncStatus() { - try { - const response = await this.client.get('/api/sync/status'); - return response.data; - } catch (error) { - console.error('failed to get sync status:', error); - return null; - } - } - - async getFullUserData() { - try { - const response = await this.client.get('/api/user/full'); - return response.data; - } catch (error) { - console.error('failed to get full user data:', error); - return null; - } - } -} - -const apiClient = new APIClient(); - -module.exports = apiClient; -module.exports = apiClient; \ No newline at end of file diff --git a/src/common/services/authService.js b/src/common/services/authService.js new file mode 100644 index 0000000..a664ba1 --- /dev/null +++ b/src/common/services/authService.js @@ -0,0 +1,160 @@ +const { onAuthStateChanged, signInWithCustomToken, signOut } = require('firebase/auth'); +const { BrowserWindow } = require('electron'); +const { getFirebaseAuth } = require('./firebaseClient'); +const userRepository = require('../repositories/user'); +const fetch = require('node-fetch'); + +async function getVirtualKeyByEmail(email, idToken) { + if (!idToken) { + throw new Error('Firebase ID token is required for virtual key request'); + } + + const resp = await fetch('https://serverless-api-sf3o.vercel.app/api/virtual_key', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${idToken}`, + }, + body: JSON.stringify({ email: email.trim().toLowerCase() }), + redirect: 'follow', + }); + + const json = await resp.json().catch(() => ({})); + if (!resp.ok) { + console.error('[VK] API request failed:', json.message || 'Unknown error'); + throw new Error(json.message || `HTTP ${resp.status}: Virtual key request failed`); + } + + const vKey = json?.data?.virtualKey || json?.data?.virtual_key || json?.data?.newVKey?.slug; + + if (!vKey) throw new Error('virtual key missing in response'); + return vKey; +} + +class AuthService { + constructor() { + this.currentUserId = 'default_user'; + this.currentUserMode = 'local'; // 'local' or 'firebase' + this.currentUser = null; + this.isInitialized = false; + } + + initialize() { + if (this.isInitialized) return; + + const auth = getFirebaseAuth(); + onAuthStateChanged(auth, async (user) => { + const previousUser = this.currentUser; + + if (user) { + // User signed IN + console.log(`[AuthService] Firebase user signed in:`, user.uid); + this.currentUser = user; + this.currentUserId = user.uid; + this.currentUserMode = 'firebase'; + + // Start background task to fetch and save virtual key + (async () => { + try { + const idToken = await user.getIdToken(true); + const virtualKey = await getVirtualKeyByEmail(user.email, idToken); + + if (global.modelStateService) { + global.modelStateService.setFirebaseVirtualKey(virtualKey); + } + console.log(`[AuthService] BG: Virtual key for ${user.email} has been processed.`); + + } catch (error) { + console.error('[AuthService] BG: Failed to fetch or save virtual key:', error); + } + })(); + + } else { + // User signed OUT + console.log(`[AuthService] No Firebase user.`); + if (previousUser) { + console.log(`[AuthService] Clearing API key for logged-out user: ${previousUser.uid}`); + if (global.modelStateService) { + global.modelStateService.setFirebaseVirtualKey(null); + } + } + this.currentUser = null; + this.currentUserId = 'default_user'; + this.currentUserMode = 'local'; + } + this.broadcastUserState(); + }); + + this.isInitialized = true; + console.log('[AuthService] Initialized and attached to Firebase Auth state.'); + } + + async signInWithCustomToken(token) { + const auth = getFirebaseAuth(); + try { + const userCredential = await signInWithCustomToken(auth, token); + console.log(`[AuthService] Successfully signed in with custom token for user:`, userCredential.user.uid); + // onAuthStateChanged will handle the state update and broadcast + } catch (error) { + console.error('[AuthService] Error signing in with custom token:', error); + throw error; // Re-throw to be handled by the caller + } + } + + async signOut() { + const auth = getFirebaseAuth(); + try { + await signOut(auth); + console.log('[AuthService] User sign-out initiated successfully.'); + // onAuthStateChanged will handle the state update and broadcast, + // which will also re-evaluate the API key status. + } catch (error) { + console.error('[AuthService] Error signing out:', error); + } + } + + broadcastUserState() { + const userState = this.getCurrentUser(); + console.log('[AuthService] Broadcasting user state change:', userState); + BrowserWindow.getAllWindows().forEach(win => { + if (win && !win.isDestroyed() && win.webContents && !win.webContents.isDestroyed()) { + win.webContents.send('user-state-changed', userState); + } + }); + } + + + getCurrentUserId() { + return this.currentUserId; + } + + getCurrentUser() { + const isLoggedIn = !!(this.currentUserMode === 'firebase' && this.currentUser); + + if (isLoggedIn) { + return { + uid: this.currentUser.uid, + email: this.currentUser.email, + displayName: this.currentUser.displayName, + mode: 'firebase', + isLoggedIn: true, + //////// before_modelStateService //////// + // hasApiKey: this.hasApiKey // Always true for firebase users, but good practice + //////// before_modelStateService //////// + }; + } + return { + uid: this.currentUserId, // returns 'default_user' + email: 'contact@pickle.com', + displayName: 'Default User', + mode: 'local', + isLoggedIn: false, + //////// before_modelStateService //////// + // hasApiKey: this.hasApiKey + //////// before_modelStateService //////// + }; + } +} + +const authService = new AuthService(); +module.exports = authService; \ No newline at end of file diff --git a/src/common/services/dataService.js b/src/common/services/dataService.js deleted file mode 100644 index 6ebc334..0000000 --- a/src/common/services/dataService.js +++ /dev/null @@ -1,158 +0,0 @@ -const config = require('../config/config'); - -class DataService { - constructor() { - this.cache = new Map(); - this.cacheTimeout = config.get('cacheTimeout'); - this.enableCaching = config.get('enableCaching'); - this.sqliteClient = null; - this.currentUserId = 'default_user'; - this.isInitialized = false; - - if (config.get('enableSQLiteStorage')) { - try { - this.sqliteClient = require('./sqliteClient'); - console.log('[DataService] SQLite storage enabled.'); - } catch (error) { - console.error('[DataService] Failed to load SQLite client:', error); - } - } - } - - async initialize() { - if (this.isInitialized || !this.sqliteClient) { - return; - } - - try { - await this.sqliteClient.connect(); - this.isInitialized = true; - console.log('[DataService] Initialized successfully'); - } catch (error) { - console.error('[DataService] Failed to initialize:', error); - throw error; - } - } - - setCurrentUser(uid) { - if (this.currentUserId !== uid) { - console.log(`[DataService] Current user switched to: ${uid}`); - this.currentUserId = uid; - this.clearCache(); - } - } - - getCacheKey(operation, params = '') { - return `${this.currentUserId}:${operation}:${params}`; - } - - getFromCache(key) { - if (!this.enableCaching) return null; - const cached = this.cache.get(key); - if (cached && (Date.now() - cached.timestamp) < this.cacheTimeout) { - return cached.data; - } - return null; - } - - setCache(key, data) { - if (!this.enableCaching) return; - this.cache.set(key, { data, timestamp: Date.now() }); - } - - clearCache() { - this.cache.clear(); - } - - async findOrCreateUser(firebaseUser) { - if (!this.sqliteClient) { - console.log('[DataService] SQLite client not available, skipping user creation'); - return firebaseUser; - } - - try { - await this.initialize(); - const existingUser = await this.sqliteClient.getUser(firebaseUser.uid); - - if (!existingUser) { - console.log(`[DataService] Creating new user in local DB: ${firebaseUser.uid}`); - await this.sqliteClient.findOrCreateUser({ - uid: firebaseUser.uid, - display_name: firebaseUser.displayName || firebaseUser.display_name, - email: firebaseUser.email - }); - } - - this.clearCache(); - return firebaseUser; - } catch (error) { - console.error('[DataService] Failed to sync Firebase user to local DB:', error); - return firebaseUser; - } - } - - async saveApiKey(apiKey) { - if (!this.sqliteClient) { - throw new Error("SQLite client not available."); - } - try { - await this.initialize(); - const result = await this.sqliteClient.saveApiKey(apiKey, this.currentUserId); - this.clearCache(); - return result; - } catch (error) { - console.error('[DataService] Failed to save API key to SQLite:', error); - throw error; - } - } - - async checkApiKey() { - if (!this.sqliteClient) return { hasApiKey: false }; - try { - await this.initialize(); - const user = await this.sqliteClient.getUser(this.currentUserId); - return { hasApiKey: !!user?.api_key && user.api_key.length > 0 }; - } catch (error) { - console.error('[DataService] Failed to check API key from SQLite:', error); - return { hasApiKey: false }; - } - } - - async getUserPresets() { - const cacheKey = this.getCacheKey('presets'); - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - if (!this.sqliteClient) return []; - try { - await this.initialize(); - const presets = await this.sqliteClient.getPresets(this.currentUserId); - this.setCache(cacheKey, presets); - return presets; - } catch (error) { - console.error('[DataService] Failed to get presets from SQLite:', error); - return []; - } - } - - async getPresetTemplates() { - const cacheKey = this.getCacheKey('preset_templates'); - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - if (!this.sqliteClient) return []; - try { - await this.initialize(); - const templates = await this.sqliteClient.getPresetTemplates(); - this.setCache(cacheKey, templates); - return templates; - } catch (error) { - console.error('[DataService] Failed to get preset templates from SQLite:', error); - return []; - } - } -} - -const dataService = new DataService(); - -module.exports = dataService; \ No newline at end of file diff --git a/src/common/services/firebaseClient.js b/src/common/services/firebaseClient.js new file mode 100644 index 0000000..5ccb6d5 --- /dev/null +++ b/src/common/services/firebaseClient.js @@ -0,0 +1,103 @@ +const { initializeApp } = require('firebase/app'); +const { initializeAuth } = require('firebase/auth'); +const Store = require('electron-store'); + +/** + * Firebase Auth expects the `persistence` option passed to `initializeAuth()` to be *classes*, + * not instances. It then calls `new PersistenceClass()` internally. + * + * The helper below returns such a class, pre-configured with an `electron-store` instance that + * will be shared across all constructed objects. This mirrors the pattern used by Firebase's own + * `browserLocalPersistence` implementation as well as community solutions for NodeJS. + */ +function createElectronStorePersistence(storeName = 'firebase-auth-session') { + // Create a single `electron-store` behind the scenes – all Persistence instances will use it. + const sharedStore = new Store({ name: storeName }); + + return class ElectronStorePersistence { + constructor() { + this.store = sharedStore; + this.type = 'LOCAL'; + } + + /** + * Firebase calls this to check whether the persistence is usable in the current context. + */ + _isAvailable() { + return Promise.resolve(true); + } + + async _set(key, value) { + this.store.set(key, value); + } + + async _get(key) { + return this.store.get(key) ?? null; + } + + async _remove(key) { + this.store.delete(key); + } + + /** + * These are used by Firebase to react to external storage events (e.g. multi-tab). + * Electron apps are single-renderer per process, so we can safely provide no-op + * implementations. + */ + _addListener(_key, _listener) { + // no-op + } + + _removeListener(_key, _listener) { + // no-op + } + }; +} + +const firebaseConfig = { + apiKey: 'AIzaSyAgtJrmsFWG1C7m9S55HyT1laICEzuUS2g', + authDomain: 'pickle-3651a.firebaseapp.com', + projectId: 'pickle-3651a', + storageBucket: 'pickle-3651a.firebasestorage.app', + messagingSenderId: '904706892885', + appId: '1:904706892885:web:0e42b3dda796674ead20dc', + measurementId: 'G-SQ0WM6S28T', +}; + +let firebaseApp = null; +let firebaseAuth = null; + +function initializeFirebase() { + if (firebaseApp) { + console.log('[FirebaseClient] Firebase already initialized.'); + return; + } + try { + firebaseApp = initializeApp(firebaseConfig); + + // Build a *class* persistence provider and hand it to Firebase. + const ElectronStorePersistence = createElectronStorePersistence('firebase-auth-session'); + + firebaseAuth = initializeAuth(firebaseApp, { + // `initializeAuth` accepts a single class or an array – we pass an array for future + // extensibility and to match Firebase examples. + persistence: [ElectronStorePersistence], + }); + + console.log('[FirebaseClient] Firebase initialized successfully with class-based electron-store persistence.'); + } catch (error) { + console.error('[FirebaseClient] Firebase initialization failed:', error); + } +} + +function getFirebaseAuth() { + if (!firebaseAuth) { + throw new Error("Firebase Auth has not been initialized. Call initializeFirebase() first."); + } + return firebaseAuth; +} + +module.exports = { + initializeFirebase, + getFirebaseAuth, +}; \ No newline at end of file diff --git a/src/common/services/googleGeminiClient.js b/src/common/services/googleGeminiClient.js deleted file mode 100644 index 877c82e..0000000 --- a/src/common/services/googleGeminiClient.js +++ /dev/null @@ -1,171 +0,0 @@ -const { GoogleGenerativeAI } = require('@google/generative-ai'); -const { GoogleGenAI } = require('@google/genai'); - -/** - * Creates and returns a Google Gemini client instance for generative AI. - * @param {string} apiKey - The API key for authentication. - * @returns {GoogleGenerativeAI} The initialized Gemini client. - */ -function createGeminiClient(apiKey) { - return new GoogleGenerativeAI(apiKey); -} - -/** - * Gets a Gemini model for text/image generation. - * @param {GoogleGenerativeAI} client - The Gemini client instance. - * @param {string} [model='gemini-2.5-flash'] - The name for the text/vision model. - * @returns {object} Model object with generateContent method - */ -function getGeminiGenerativeModel(client, model = 'gemini-2.5-flash') { - const genAI = client; - const geminiModel = genAI.getGenerativeModel({ model: model }); - - return { - generateContent: async (parts) => { - let systemPrompt = ''; - let userContent = []; - - for (const part of parts) { - if (typeof part === 'string') { - if (systemPrompt === '' && part.includes('You are')) { - systemPrompt = part; - } else { - userContent.push(part); - } - } else if (part.inlineData) { - // Convert base64 image data to Gemini format - userContent.push({ - inlineData: { - mimeType: part.inlineData.mimeType, - data: part.inlineData.data - } - }); - } - } - - // Prepare content array - const content = []; - - // Add system instruction if present - if (systemPrompt) { - // For Gemini, we'll prepend system prompt to user content - content.push(systemPrompt + '\n\n' + userContent[0]); - content.push(...userContent.slice(1)); - } else { - content.push(...userContent); - } - - try { - const result = await geminiModel.generateContent(content); - const response = await result.response; - - return { - response: { - text: () => response.text() - } - }; - } catch (error) { - console.error('Gemini API error:', error); - throw error; - } - } - }; -} - -/** - * Creates a Gemini chat session for multi-turn conversations. - * @param {GoogleGenerativeAI} client - The Gemini client instance. - * @param {string} [model='gemini-2.5-flash'] - The model to use. - * @param {object} [config={}] - Configuration options. - * @returns {object} Chat session object - */ -function createGeminiChat(client, model = 'gemini-2.5-flash', config = {}) { - const genAI = client; - const geminiModel = genAI.getGenerativeModel({ - model: model, - systemInstruction: config.systemInstruction - }); - - const chat = geminiModel.startChat({ - history: config.history || [], - generationConfig: { - temperature: config.temperature || 0.7, - maxOutputTokens: config.maxOutputTokens || 8192, - } - }); - - return { - sendMessage: async (message) => { - const result = await chat.sendMessage(message); - const response = await result.response; - return { - text: response.text() - }; - }, - sendMessageStream: async function* (message) { - const result = await chat.sendMessageStream(message); - for await (const chunk of result.stream) { - yield { - text: chunk.text() - }; - } - }, - getHistory: () => chat.getHistory() - }; -} - -// async function connectToGeminiSession(apiKey, { language = 'en-US', callbacks = {} } = {}) { -// const liveClient = new GoogleGenAI({ -// vertexai: false, // Vertex AI μ‚¬μš© μ•ˆν•¨ -// apiKey, -// }); - -// // 라이브 STT μ„Έμ…˜ μ—΄κΈ° -// const session = await liveClient.live.connect({ -// model: 'gemini-live-2.5-flash-preview', -// callbacks, -// config: { -// inputAudioTranscription: {}, // μ‹€μ‹œκ°„ STT ν•„μˆ˜ -// speechConfig: { languageCode: language }, -// }, -// }); - -// return { -// sendRealtimeInput: async data => session.send({ -// audio: { data, mimeType: 'audio/pcm;rate=24000' } -// }), -// close: async () => session.close(), -// }; -// } - -async function connectToGeminiSession(apiKey, { language = 'en-US', callbacks = {} } = {}) { - // β‘  μ˜›λ‚  μŠ€νƒ€μΌ helper μž¬μ‚¬μš© - const liveClient = new GoogleGenAI({ vertexai: false, apiKey }); - - // β‘‘ μ–Έμ–΄ μ½”λ“œ κ°•μ œ BCP-47 λ³€ν™˜ - const lang = language.includes('-') ? language : `${language}-US`; - - const session = await liveClient.live.connect({ - model: 'gemini-live-2.5-flash-preview', - callbacks, - config: { - inputAudioTranscription: {}, - speechConfig: { languageCode: lang }, - }, - }); - - // β‘’ SDK 0.5+ : sendRealtimeInput κ°€ 정식 이름 - return { - sendRealtimeInput: async payload => session.sendRealtimeInput(payload), - close: async () => session.close(), - }; - } - - - -module.exports = { - createGeminiClient, - getGeminiGenerativeModel, - createGeminiChat, - connectToGeminiSession, -}; \ No newline at end of file diff --git a/src/common/services/modelStateService.js b/src/common/services/modelStateService.js new file mode 100644 index 0000000..7218ce7 --- /dev/null +++ b/src/common/services/modelStateService.js @@ -0,0 +1,329 @@ +const Store = require('electron-store'); +const fetch = require('node-fetch'); +const { ipcMain, webContents } = require('electron'); +const { PROVIDERS } = require('../ai/factory'); + +class ModelStateService { + constructor(authService) { + this.authService = authService; + this.store = new Store({ name: 'pickle-glass-model-state' }); + this.state = {}; + } + + initialize() { + this._loadStateForCurrentUser(); + + this.setupIpcHandlers(); + console.log('[ModelStateService] Initialized.'); + } + + _logCurrentSelection() { + const llmModel = this.state.selectedModels.llm; + const sttModel = this.state.selectedModels.stt; + const llmProvider = this.getProviderForModel('llm', llmModel) || 'None'; + const sttProvider = this.getProviderForModel('stt', sttModel) || 'None'; + + console.log(`[ModelStateService] 🌟 Current Selection -> LLM: ${llmModel || 'None'} (Provider: ${llmProvider}), STT: ${sttModel || 'None'} (Provider: ${sttProvider})`); + } + + _autoSelectAvailableModels() { + console.log('[ModelStateService] Running auto-selection for models...'); + const types = ['llm', 'stt']; + + types.forEach(type => { + const currentModelId = this.state.selectedModels[type]; + let isCurrentModelValid = false; + + if (currentModelId) { + const provider = this.getProviderForModel(type, currentModelId); + if (provider && this.getApiKey(provider)) { + isCurrentModelValid = true; + } + } + + if (!isCurrentModelValid) { + console.log(`[ModelStateService] No valid ${type.toUpperCase()} model selected. Finding an alternative...`); + const availableModels = this.getAvailableModels(type); + if (availableModels.length > 0) { + this.state.selectedModels[type] = availableModels[0].id; + console.log(`[ModelStateService] Auto-selected ${type.toUpperCase()} model: ${availableModels[0].id}`); + } else { + this.state.selectedModels[type] = null; + } + } + }); + } + + _loadStateForCurrentUser() { + const userId = this.authService.getCurrentUserId(); + const initialApiKeys = Object.keys(PROVIDERS).reduce((acc, key) => { + acc[key] = null; + return acc; + }, {}); + + const defaultState = { + apiKeys: initialApiKeys, + selectedModels: { llm: null, stt: null }, + }; + this.state = this.store.get(`users.${userId}`, defaultState); + console.log(`[ModelStateService] State loaded for user: ${userId}`); + for (const p of Object.keys(PROVIDERS)) { + if (!(p in this.state.apiKeys)) { + this.state.apiKeys[p] = null; + } + } + this._autoSelectAvailableModels(); + this._saveState(); + this._logCurrentSelection(); + } + + + _saveState() { + const userId = this.authService.getCurrentUserId(); + this.store.set(`users.${userId}`, this.state); + console.log(`[ModelStateService] State saved for user: ${userId}`); + this._logCurrentSelection(); + } + + async validateApiKey(provider, key) { + if (!key || key.trim() === '') { + return { success: false, error: 'API key cannot be empty.' }; + } + + let validationUrl, headers; + const body = undefined; + + switch (provider) { + case 'openai': + validationUrl = 'https://api.openai.com/v1/models'; + headers = { 'Authorization': `Bearer ${key}` }; + break; + case 'gemini': + validationUrl = `https://generativelanguage.googleapis.com/v1beta/models?key=${key}`; + headers = {}; + break; + case 'anthropic': { + if (!key.startsWith('sk-ant-')) { + throw new Error('Invalid Anthropic key format.'); + } + const response = await fetch("https://api.anthropic.com/v1/messages", { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": key, + "anthropic-version": "2023-06-01", + }, + body: JSON.stringify({ + model: "claude-3-haiku-20240307", + max_tokens: 1, + messages: [{ role: "user", content: "Hi" }], + }), + }); + + if (!response.ok && response.status !== 400) { + const errorData = await response.json().catch(() => ({})); + return { success: false, error: errorData.error?.message || `Validation failed with status: ${response.status}` }; + } + + console.log(`[ModelStateService] API key for ${provider} is valid.`); + this.setApiKey(provider, key); + return { success: true }; + } + default: + return { success: false, error: 'Unknown provider.' }; + } + + try { + const response = await fetch(validationUrl, { headers, body }); + if (response.ok) { + console.log(`[ModelStateService] API key for ${provider} is valid.`); + this.setApiKey(provider, key); + return { success: true }; + } else { + const errorData = await response.json().catch(() => ({})); + const message = errorData.error?.message || `Validation failed with status: ${response.status}`; + console.log(`[ModelStateService] API key for ${provider} is invalid: ${message}`); + return { success: false, error: message }; + } + } catch (error) { + console.error(`[ModelStateService] Network error during ${provider} key validation:`, error); + return { success: false, error: 'A network error occurred during validation.' }; + } + } + + setFirebaseVirtualKey(virtualKey) { + console.log(`[ModelStateService] Setting Firebase virtual key (for openai-glass).`); + this.state.apiKeys['openai-glass'] = virtualKey; + + const llmModels = PROVIDERS['openai-glass']?.llmModels; + const sttModels = PROVIDERS['openai-glass']?.sttModels; + + if (!this.state.selectedModels.llm && llmModels?.length > 0) { + this.state.selectedModels.llm = llmModels[0].id; + } + if (!this.state.selectedModels.stt && sttModels?.length > 0) { + this.state.selectedModels.stt = sttModels[0].id; + } + this._autoSelectAvailableModels(); + this._saveState(); + this._logCurrentSelection(); + } + + setApiKey(provider, key) { + if (provider in this.state.apiKeys) { + this.state.apiKeys[provider] = key; + + const llmModels = PROVIDERS[provider]?.llmModels; + const sttModels = PROVIDERS[provider]?.sttModels; + + if (!this.state.selectedModels.llm && llmModels?.length > 0) { + this.state.selectedModels.llm = llmModels[0].id; + } + if (!this.state.selectedModels.stt && sttModels?.length > 0) { + this.state.selectedModels.stt = sttModels[0].id; + } + this._saveState(); + this._logCurrentSelection(); + return true; + } + return false; + } + + getApiKey(provider) { + return this.state.apiKeys[provider] || null; + } + + getAllApiKeys() { + const { 'openai-glass': _, ...displayKeys } = this.state.apiKeys; + return displayKeys; + } + + removeApiKey(provider) { + if (provider in this.state.apiKeys) { + this.state.apiKeys[provider] = null; + const llmProvider = this.getProviderForModel('llm', this.state.selectedModels.llm); + if (llmProvider === provider) this.state.selectedModels.llm = null; + + const sttProvider = this.getProviderForModel('stt', this.state.selectedModels.stt); + if (sttProvider === provider) this.state.selectedModels.stt = null; + + this._autoSelectAvailableModels(); + this._saveState(); + this._logCurrentSelection(); + return true; + } + return false; + } + + getProviderForModel(type, modelId) { + if (!modelId) return null; + for (const providerId in PROVIDERS) { + const models = type === 'llm' ? PROVIDERS[providerId].llmModels : PROVIDERS[providerId].sttModels; + if (models.some(m => m.id === modelId)) { + return providerId; + } + } + return null; + } + + getCurrentProvider(type) { + const selectedModel = this.state.selectedModels[type]; + return this.getProviderForModel(type, selectedModel); + } + + isLoggedInWithFirebase() { + return this.authService.getCurrentUser().isLoggedIn; + } + + areProvidersConfigured() { + if (this.isLoggedInWithFirebase()) return true; + + // LLMκ³Ό STT λͺ¨λΈμ„ μ œκ³΅ν•˜λŠ” Provider 쀑 ν•˜λ‚˜λΌλ„ API ν‚€κ°€ μ„€μ •λ˜μ—ˆλŠ”μ§€ 확인 + const hasLlmKey = Object.entries(this.state.apiKeys).some(([provider, key]) => key && PROVIDERS[provider]?.llmModels.length > 0); + const hasSttKey = Object.entries(this.state.apiKeys).some(([provider, key]) => key && PROVIDERS[provider]?.sttModels.length > 0); + + return hasLlmKey && hasSttKey; + } + + + getAvailableModels(type) { + const available = []; + const modelList = type === 'llm' ? 'llmModels' : 'sttModels'; + + Object.entries(this.state.apiKeys).forEach(([providerId, key]) => { + if (key && PROVIDERS[providerId]?.[modelList]) { + available.push(...PROVIDERS[providerId][modelList]); + } + }); + return [...new Map(available.map(item => [item.id, item])).values()]; + } + + getSelectedModels() { + return this.state.selectedModels; + } + + setSelectedModel(type, modelId) { + const provider = this.getProviderForModel(type, modelId); + if (provider && this.state.apiKeys[provider]) { + this.state.selectedModels[type] = modelId; + this._saveState(); + return true; + } + return false; + } + + /** + * + * @param {('llm' | 'stt')} type + * @returns {{provider: string, model: string, apiKey: string} | null} + */ + getCurrentModelInfo(type) { + this._logCurrentSelection(); + const model = this.state.selectedModels[type]; + if (!model) { + return null; + } + + const provider = this.getProviderForModel(type, model); + if (!provider) { + return null; + } + + const apiKey = this.getApiKey(provider); + return { provider, model, apiKey }; + } + + setupIpcHandlers() { + ipcMain.handle('model:validate-key', (e, { provider, key }) => this.validateApiKey(provider, key)); + ipcMain.handle('model:get-all-keys', () => this.getAllApiKeys()); + ipcMain.handle('model:set-api-key', (e, { provider, key }) => this.setApiKey(provider, key)); + ipcMain.handle('model:remove-api-key', (e, { provider }) => { + const success = this.removeApiKey(provider); + if (success) { + const selectedModels = this.getSelectedModels(); + if (!selectedModels.llm || !selectedModels.stt) { + webContents.getAllWebContents().forEach(wc => { + wc.send('force-show-apikey-header'); + }); + } + } + return success; + }); + ipcMain.handle('model:get-selected-models', () => this.getSelectedModels()); + ipcMain.handle('model:set-selected-model', (e, { type, modelId }) => this.setSelectedModel(type, modelId)); + ipcMain.handle('model:get-available-models', (e, { type }) => this.getAvailableModels(type)); + ipcMain.handle('model:are-providers-configured', () => this.areProvidersConfigured()); + ipcMain.handle('model:get-current-model-info', (e, { type }) => this.getCurrentModelInfo(type)); + + ipcMain.handle('model:get-provider-config', () => { + const serializableProviders = {}; + for (const key in PROVIDERS) { + const { handler, ...rest } = PROVIDERS[key]; + serializableProviders[key] = rest; + } + return serializableProviders; + }); + } +} + +module.exports = ModelStateService; \ No newline at end of file diff --git a/src/common/services/openAiClient.js b/src/common/services/openAiClient.js deleted file mode 100644 index 56a9141..0000000 --- a/src/common/services/openAiClient.js +++ /dev/null @@ -1,177 +0,0 @@ -const OpenAI = require('openai'); -const WebSocket = require('ws'); - -/** - * Creates and returns an OpenAI client instance for STT (Speech-to-Text). - * @param {string} apiKey - The API key for authentication. - * @returns {OpenAI} The initialized OpenAI client. - */ -function createOpenAiClient(apiKey) { - return new OpenAI({ - apiKey: apiKey, - }); -} - -/** - * Creates and returns an OpenAI client instance for text/image generation. - * @param {string} apiKey - The API key for authentication. - * @returns {OpenAI} The initialized OpenAI client. - */ -function createOpenAiGenerativeClient(apiKey) { - return new OpenAI({ - apiKey: apiKey, - }); -} - -/** - * Connects to an OpenAI Realtime WebSocket session for STT. - * @param {string} key - Portkey vKey or OpenAI apiKey. - * @param {object} config - The configuration object for the realtime session. - * @param {'apiKey'|'vKey'} keyType - key type ('apiKey' | 'vKey'). - * @returns {Promise} A promise that resolves to the session object with send and close methods. - */ -async function connectToOpenAiSession(key, config, keyType) { - if (keyType !== 'apiKey' && keyType !== 'vKey') { - throw new Error('keyType must be either "apiKey" or "vKey".'); - } - - const wsUrl = keyType === 'apiKey' - ? 'wss://api.openai.com/v1/realtime?intent=transcription' - : 'wss://api.portkey.ai/v1/realtime?intent=transcription'; - - const headers = keyType === 'apiKey' - ? { - 'Authorization': `Bearer ${key}`, - 'OpenAI-Beta' : 'realtime=v1', - } - : { - 'x-portkey-api-key' : 'gRv2UGRMq6GGLJ8aVEB4e7adIewu', - 'x-portkey-virtual-key': key, - 'OpenAI-Beta' : 'realtime=v1', - }; - - const ws = new WebSocket(wsUrl, { headers }); - - return new Promise((resolve, reject) => { - ws.onopen = () => { - console.log("WebSocket session opened."); - - const sessionConfig = { - type: 'transcription_session.update', - session: { - input_audio_format: 'pcm16', - input_audio_transcription: { - model: 'gpt-4o-mini-transcribe', - prompt: config.prompt || '', - language: config.language || 'en' - }, - turn_detection: { - type: 'server_vad', - threshold: 0.5, - prefix_padding_ms: 50, - silence_duration_ms: 25, - }, - input_audio_noise_reduction: { - type: 'near_field' - } - } - }; - - ws.send(JSON.stringify(sessionConfig)); - - resolve({ - sendRealtimeInput: (audioData) => { - if (ws.readyState === WebSocket.OPEN) { - const message = { - type: 'input_audio_buffer.append', - audio: audioData - }; - ws.send(JSON.stringify(message)); - } - }, - close: () => { - if (ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify({ type: 'session.close' })); - ws.close(1000, 'Client initiated close.'); - } - } - }); - }; - - ws.onmessage = (event) => { - const message = JSON.parse(event.data); - if (config.callbacks && config.callbacks.onmessage) { - config.callbacks.onmessage(message); - } - }; - - ws.onerror = (error) => { - console.error('WebSocket error:', error.message); - if (config.callbacks && config.callbacks.onerror) { - config.callbacks.onerror(error); - } - reject(error); - }; - - ws.onclose = (event) => { - console.log(`WebSocket closed: ${event.code} ${event.reason}`); - if (config.callbacks && config.callbacks.onclose) { - config.callbacks.onclose(event); - } - }; - }); -} - -/** - * Gets a GPT model for text/image generation. - * @param {OpenAI} client - The OpenAI client instance. - * @param {string} [model='gpt-4.1'] - The name for the text/vision model. - * @returns {object} Model object with generateContent method - */ -function getOpenAiGenerativeModel(client, model = 'gpt-4.1') { - return { - generateContent: async (parts) => { - const messages = []; - let systemPrompt = ''; - let userContent = []; - - for (const part of parts) { - if (typeof part === 'string') { - if (systemPrompt === '' && part.includes('You are')) { - systemPrompt = part; - } else { - userContent.push({ type: 'text', text: part }); - } - } else if (part.inlineData) { - userContent.push({ - type: 'image_url', - image_url: { url: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}` } - }); - } - } - - if (systemPrompt) messages.push({ role: 'system', content: systemPrompt }); - if (userContent.length > 0) messages.push({ role: 'user', content: userContent }); - - const response = await client.chat.completions.create({ - model: model, - messages: messages, - temperature: 0.7, - max_tokens: 2048 - }); - - return { - response: { - text: () => response.choices[0].message.content - } - }; - } - }; -} - -module.exports = { - createOpenAiClient, - connectToOpenAiSession, - createOpenAiGenerativeClient, - getOpenAiGenerativeModel, -}; \ No newline at end of file diff --git a/src/common/services/sqliteClient.js b/src/common/services/sqliteClient.js index d91ccee..afd64f3 100644 --- a/src/common/services/sqliteClient.js +++ b/src/common/services/sqliteClient.js @@ -1,4 +1,4 @@ -const sqlite3 = require('sqlite3').verbose(); +const Database = require('better-sqlite3'); const path = require('path'); const LATEST_SCHEMA = require('../config/schema'); @@ -10,104 +10,79 @@ class SQLiteClient { } connect(dbPath) { - return new Promise((resolve, reject) => { - if (this.db) { - console.log('[SQLiteClient] Already connected.'); - return resolve(); - } + if (this.db) { + console.log('[SQLiteClient] Already connected.'); + return; + } + try { this.dbPath = dbPath; - this.db = new sqlite3.Database(this.dbPath, (err) => { - if (err) { - console.error('[SQLiteClient] Could not connect to database', err); - return reject(err); - } - console.log('[SQLiteClient] Connected successfully to:', this.dbPath); - - this.db.run('PRAGMA journal_mode = WAL;', (err) => { - if (err) { - return reject(err); - } - resolve(); - }); - }); - }); + this.db = new Database(this.dbPath); + this.db.pragma('journal_mode = WAL'); + console.log('[SQLiteClient] Connected successfully to:', this.dbPath); + } catch (err) { + console.error('[SQLiteClient] Could not connect to database', err); + throw err; + } } - async synchronizeSchema() { + getDb() { + if (!this.db) { + throw new Error("Database not connected. Call connect() first."); + } + return this.db; + } + + synchronizeSchema() { console.log('[DB Sync] Starting schema synchronization...'); - const tablesInDb = await this.getTablesFromDb(); + const tablesInDb = this.getTablesFromDb(); for (const tableName of Object.keys(LATEST_SCHEMA)) { const tableSchema = LATEST_SCHEMA[tableName]; if (!tablesInDb.includes(tableName)) { // Table doesn't exist, create it - await this.createTable(tableName, tableSchema); + this.createTable(tableName, tableSchema); } else { // Table exists, check for missing columns - await this.updateTable(tableName, tableSchema); + this.updateTable(tableName, tableSchema); } } console.log('[DB Sync] Schema synchronization finished.'); } - async getTablesFromDb() { - return new Promise((resolve, reject) => { - this.db.all("SELECT name FROM sqlite_master WHERE type='table'", (err, tables) => { - if (err) return reject(err); - resolve(tables.map(t => t.name)); - }); - }); + getTablesFromDb() { + const tables = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all(); + return tables.map(t => t.name); } - async createTable(tableName, tableSchema) { - return new Promise((resolve, reject) => { - const columnDefs = tableSchema.columns.map(col => `"${col.name}" ${col.type}`).join(', '); - const query = `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnDefs})`; + createTable(tableName, tableSchema) { + const columnDefs = tableSchema.columns.map(col => `"${col.name}" ${col.type}`).join(', '); + const query = `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnDefs})`; - console.log(`[DB Sync] Creating table: ${tableName}`); - this.db.run(query, (err) => { - if (err) return reject(err); - resolve(); - }); - }); + console.log(`[DB Sync] Creating table: ${tableName}`); + this.db.prepare(query).run(); } - async updateTable(tableName, tableSchema) { - return new Promise((resolve, reject) => { - this.db.all(`PRAGMA table_info("${tableName}")`, async (err, existingColumns) => { - if (err) return reject(err); + updateTable(tableName, tableSchema) { + const existingColumns = this.db.prepare(`PRAGMA table_info("${tableName}")`).all(); + const existingColumnNames = existingColumns.map(c => c.name); + const columnsToAdd = tableSchema.columns.filter(col => !existingColumnNames.includes(col.name)); - const existingColumnNames = existingColumns.map(c => c.name); - const columnsToAdd = tableSchema.columns.filter(col => !existingColumnNames.includes(col.name)); - - if (columnsToAdd.length > 0) { - console.log(`[DB Sync] Updating table: ${tableName}. Adding columns: ${columnsToAdd.map(c=>c.name).join(', ')}`); - for (const column of columnsToAdd) { - const addColumnQuery = `ALTER TABLE "${tableName}" ADD COLUMN "${column.name}" ${column.type}`; - try { - await this.runQuery(addColumnQuery); - } catch (alterErr) { - return reject(alterErr); - } - } - } - resolve(); - }); - }); + if (columnsToAdd.length > 0) { + console.log(`[DB Sync] Updating table: ${tableName}. Adding columns: ${columnsToAdd.map(c=>c.name).join(', ')}`); + for (const column of columnsToAdd) { + const addColumnQuery = `ALTER TABLE "${tableName}" ADD COLUMN "${column.name}" ${column.type}`; + this.db.prepare(addColumnQuery).run(); + } + } } - async runQuery(query, params = []) { - return new Promise((resolve, reject) => { - this.db.run(query, params, function(err) { - if (err) return reject(err); - resolve(this); - }); - }); + runQuery(query, params = []) { + return this.db.prepare(query).run(params); } - async cleanupEmptySessions() { + cleanupEmptySessions() { console.log('[DB Cleanup] Checking for empty sessions...'); const query = ` SELECT s.id FROM sessions s @@ -117,349 +92,99 @@ class SQLiteClient { WHERE t.id IS NULL AND a.id IS NULL AND su.session_id IS NULL `; - return new Promise((resolve, reject) => { - this.db.all(query, [], (err, rows) => { - if (err) { - console.error('[DB Cleanup] Error finding empty sessions:', err); - return reject(err); - } + const rows = this.db.prepare(query).all(); - if (rows.length === 0) { - console.log('[DB Cleanup] No empty sessions found.'); - return resolve(); - } + if (rows.length === 0) { + console.log('[DB Cleanup] No empty sessions found.'); + return; + } - const idsToDelete = rows.map(r => r.id); - const placeholders = idsToDelete.map(() => '?').join(','); - const deleteQuery = `DELETE FROM sessions WHERE id IN (${placeholders})`; + const idsToDelete = rows.map(r => r.id); + const placeholders = idsToDelete.map(() => '?').join(','); + const deleteQuery = `DELETE FROM sessions WHERE id IN (${placeholders})`; - console.log(`[DB Cleanup] Found ${idsToDelete.length} empty sessions. Deleting...`); - this.db.run(deleteQuery, idsToDelete, function(deleteErr) { - if (deleteErr) { - console.error('[DB Cleanup] Error deleting empty sessions:', deleteErr); - return reject(deleteErr); - } - console.log(`[DB Cleanup] Successfully deleted ${this.changes} empty sessions.`); - resolve(); - }); - }); - }); + console.log(`[DB Cleanup] Found ${idsToDelete.length} empty sessions. Deleting...`); + const result = this.db.prepare(deleteQuery).run(idsToDelete); + console.log(`[DB Cleanup] Successfully deleted ${result.changes} empty sessions.`); } - async initTables() { - await this.synchronizeSchema(); - await this.initDefaultData(); + initTables() { + this.synchronizeSchema(); + this.initDefaultData(); } - async initDefaultData() { - return new Promise((resolve, reject) => { - const now = Math.floor(Date.now() / 1000); - const initUserQuery = ` - INSERT OR IGNORE INTO users (uid, display_name, email, created_at) - VALUES (?, ?, ?, ?) - `; + initDefaultData() { + const now = Math.floor(Date.now() / 1000); + const initUserQuery = ` + INSERT OR IGNORE INTO users (uid, display_name, email, created_at) + VALUES (?, ?, ?, ?) + `; - this.db.run(initUserQuery, [this.defaultUserId, 'Default User', 'contact@pickle.com', now], (err) => { - if (err) { - console.error('Failed to initialize default user:', err); - return reject(err); - } + this.db.prepare(initUserQuery).run(this.defaultUserId, 'Default User', 'contact@pickle.com', now); - const defaultPresets = [ - ['school', 'School', 'You are a school and lecture assistant. Your goal is to help the user, a student, understand academic material and answer questions.\n\nWhenever a question appears on the user\'s screen or is asked aloud, you provide a direct, step-by-step answer, showing all necessary reasoning or calculations.\n\nIf the user is watching a lecture or working through new material, you offer concise explanations of key concepts and clarify definitions as they come up.', 1], - ['meetings', 'Meetings', 'You are a meeting assistant. Your goal is to help the user capture key information during meetings and follow up effectively.\n\nYou help capture meeting notes, track action items, identify key decisions, and summarize important points discussed during meetings.', 1], - ['sales', 'Sales', 'You are a real-time AI sales assistant, and your goal is to help the user close deals during sales interactions.\n\nYou provide real-time sales support, suggest responses to objections, help identify customer needs, and recommend strategies to advance deals.', 1], - ['recruiting', 'Recruiting', 'You are a recruiting assistant. Your goal is to help the user interview candidates and evaluate talent effectively.\n\nYou help evaluate candidates, suggest interview questions, analyze responses, and provide insights about candidate fit for positions.', 1], - ['customer-support', 'Customer Support', 'You are a customer support assistant. Your goal is to help resolve customer issues efficiently and thoroughly.\n\nYou help diagnose customer problems, suggest solutions, provide step-by-step troubleshooting guidance, and ensure customer satisfaction.', 1], - ]; + const defaultPresets = [ + ['school', 'School', 'You are a school and lecture assistant. Your goal is to help the user, a student, understand academic material and answer questions.\n\nWhenever a question appears on the user\'s screen or is asked aloud, you provide a direct, step-by-step answer, showing all necessary reasoning or calculations.\n\nIf the user is watching a lecture or working through new material, you offer concise explanations of key concepts and clarify definitions as they come up.', 1], + ['meetings', 'Meetings', 'You are a meeting assistant. Your goal is to help the user capture key information during meetings and follow up effectively.\n\nYou help capture meeting notes, track action items, identify key decisions, and summarize important points discussed during meetings.', 1], + ['sales', 'Sales', 'You are a real-time AI sales assistant, and your goal is to help the user close deals during sales interactions.\n\nYou provide real-time sales support, suggest responses to objections, help identify customer needs, and recommend strategies to advance deals.', 1], + ['recruiting', 'Recruiting', 'You are a recruiting assistant. Your goal is to help the user interview candidates and evaluate talent effectively.\n\nYou help evaluate candidates, suggest interview questions, analyze responses, and provide insights about candidate fit for positions.', 1], + ['customer-support', 'Customer Support', 'You are a customer support assistant. Your goal is to help resolve customer issues efficiently and thoroughly.\n\nYou help diagnose customer problems, suggest solutions, provide step-by-step troubleshooting guidance, and ensure customer satisfaction.', 1], + ]; - const stmt = this.db.prepare(` - INSERT OR IGNORE INTO prompt_presets (id, uid, title, prompt, is_default, created_at) - VALUES (?, ?, ?, ?, ?, ?) - `); + const stmt = this.db.prepare(` + INSERT OR IGNORE INTO prompt_presets (id, uid, title, prompt, is_default, created_at) + VALUES (?, ?, ?, ?, ?, ?) + `); - for (const preset of defaultPresets) { - stmt.run(preset[0], this.defaultUserId, preset[1], preset[2], preset[3], now); - } + for (const preset of defaultPresets) { + stmt.run(preset[0], this.defaultUserId, preset[1], preset[2], preset[3], now); + } - stmt.finalize((err) => { - if (err) { - console.error('Failed to finalize preset statement:', err); - return reject(err); - } - console.log('Default data initialized.'); - resolve(); - }); - }); - }); + console.log('Default data initialized.'); } - async findOrCreateUser(user) { - return new Promise((resolve, reject) => { - const { uid, display_name, email } = user; - const now = Math.floor(Date.now() / 1000); - - const query = ` - INSERT INTO users (uid, display_name, email, created_at) - VALUES (?, ?, ?, ?) - ON CONFLICT(uid) DO UPDATE SET - display_name=excluded.display_name, - email=excluded.email - `; - - this.db.run(query, [uid, display_name, email, now], (err) => { - if (err) { - console.error('Failed to find or create user in SQLite:', err); - return reject(err); - } - this.getUser(uid).then(resolve).catch(reject); - }); - }); + markPermissionsAsCompleted() { + return this.query( + 'INSERT OR REPLACE INTO system_settings (key, value) VALUES (?, ?)', + ['permissions_completed', 'true'] + ); } - async getUser(uid) { - return new Promise((resolve, reject) => { - this.db.get('SELECT * FROM users WHERE uid = ?', [uid], (err, row) => { - if (err) reject(err); - else resolve(row); - }); - }); - } - - async saveApiKey(apiKey, uid = this.defaultUserId, provider = 'openai') { - return new Promise((resolve, reject) => { - this.db.run( - 'UPDATE users SET api_key = ?, provider = ? WHERE uid = ?', - [apiKey, provider, uid], - function(err) { - if (err) { - console.error('SQLite: Failed to save API key:', err); - reject(err); - } else { - console.log(`SQLite: API key saved for user ${uid} with provider ${provider}.`); - resolve({ changes: this.changes }); - } - } - ); - }); - } - - async getPresets(uid) { - return new Promise((resolve, reject) => { - const query = ` - SELECT * FROM prompt_presets - WHERE uid = ? OR is_default = 1 - ORDER BY is_default DESC, title ASC - `; - this.db.all(query, [uid], (err, rows) => { - if (err) { - console.error('SQLite: Failed to get presets:', err); - reject(err); - } else { - resolve(rows); - } - }); - }); - } - - async getPresetTemplates() { - return new Promise((resolve, reject) => { - const query = ` - SELECT * FROM prompt_presets - WHERE is_default = 1 - ORDER BY title ASC - `; - this.db.all(query, [], (err, rows) => { - if (err) { - console.error('SQLite: Failed to get preset templates:', err); - reject(err); - } else { - resolve(rows); - } - }); - }); - } - - async getSession(id) { - return new Promise((resolve, reject) => { - this.db.get('SELECT * FROM sessions WHERE id = ?', [id], (err, row) => { - if (err) reject(err); - else resolve(row); - }); - }); - } - - async updateSessionType(id, type) { - return new Promise((resolve, reject) => { - const now = Math.floor(Date.now() / 1000); - const query = 'UPDATE sessions SET session_type = ?, updated_at = ? WHERE id = ?'; - this.db.run(query, [type, now, id], function(err) { - if (err) { - reject(err); - } else { - resolve({ changes: this.changes }); - } - }); - }); - } - - async touchSession(id) { - return new Promise((resolve, reject) => { - const now = Math.floor(Date.now() / 1000); - const query = 'UPDATE sessions SET updated_at = ? WHERE id = ?'; - this.db.run(query, [now, id], function(err) { - if (err) reject(err); - else resolve({ changes: this.changes }); - }); - }); - } - - async createSession(uid, type = 'ask') { - return new Promise((resolve, reject) => { - const sessionId = require('crypto').randomUUID(); - const now = Math.floor(Date.now() / 1000); - const query = `INSERT INTO sessions (id, uid, title, session_type, started_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`; - - this.db.run(query, [sessionId, uid, `Session @ ${new Date().toLocaleTimeString()}`, type, now, now], function(err) { - if (err) { - console.error('SQLite: Failed to create session:', err); - reject(err); - } else { - console.log(`SQLite: Created session ${sessionId} for user ${uid} (type: ${type})`); - resolve(sessionId); - } - }); - }); - } - - async endSession(sessionId) { - return new Promise((resolve, reject) => { - const now = Math.floor(Date.now() / 1000); - const query = `UPDATE sessions SET ended_at = ?, updated_at = ? WHERE id = ?`; - this.db.run(query, [now, now, sessionId], function(err) { - if (err) reject(err); - else resolve({ changes: this.changes }); - }); - }); - } - - async addTranscript({ sessionId, speaker, text }) { - return new Promise((resolve, reject) => { - this.touchSession(sessionId).catch(err => console.error("Failed to touch session", err)); - const transcriptId = require('crypto').randomUUID(); - const now = Math.floor(Date.now() / 1000); - const query = `INSERT INTO transcripts (id, session_id, start_at, speaker, text, created_at) VALUES (?, ?, ?, ?, ?, ?)`; - this.db.run(query, [transcriptId, sessionId, now, speaker, text, now], function(err) { - if (err) reject(err); - else resolve({ id: transcriptId }); - }); - }); - } - - async addAiMessage({ sessionId, role, content, model = 'gpt-4.1' }) { - return new Promise((resolve, reject) => { - this.touchSession(sessionId).catch(err => console.error("Failed to touch session", err)); - const messageId = require('crypto').randomUUID(); - const now = Math.floor(Date.now() / 1000); - const query = `INSERT INTO ai_messages (id, session_id, sent_at, role, content, model, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`; - this.db.run(query, [messageId, sessionId, now, role, content, model, now], function(err) { - if (err) reject(err); - else resolve({ id: messageId }); - }); - }); - } - - async saveSummary({ sessionId, tldr, text, bullet_json, action_json, model = 'gpt-4.1' }) { - return new Promise((resolve, reject) => { - this.touchSession(sessionId).catch(err => console.error("Failed to touch session", err)); - const now = Math.floor(Date.now() / 1000); - const query = ` - INSERT INTO summaries (session_id, generated_at, model, text, tldr, bullet_json, action_json, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - ON CONFLICT(session_id) DO UPDATE SET - generated_at=excluded.generated_at, - model=excluded.model, - text=excluded.text, - tldr=excluded.tldr, - bullet_json=excluded.bullet_json, - action_json=excluded.action_json, - updated_at=excluded.updated_at - `; - this.db.run(query, [sessionId, now, model, text, tldr, bullet_json, action_json, now], function(err) { - if (err) reject(err); - else resolve({ changes: this.changes }); - }); - }); - } - - async runMigrations() { - return new Promise((resolve, reject) => { - console.log('[DB Migration] Checking schema for `sessions` table...'); - this.db.all("PRAGMA table_info(sessions)", (err, columns) => { - if (err) { - console.error('[DB Migration] Error checking sessions table schema:', err); - return reject(err); - } - - const hasSessionTypeCol = columns.some(col => col.name === 'session_type'); - - if (!hasSessionTypeCol) { - console.log('[DB Migration] `session_type` column missing. Altering table...'); - this.db.run("ALTER TABLE sessions ADD COLUMN session_type TEXT DEFAULT 'ask'", (alterErr) => { - if (alterErr) { - console.error('[DB Migration] Failed to add `session_type` column:', alterErr); - return reject(alterErr); - } - console.log('[DB Migration] `sessions` table updated successfully.'); - resolve(); - }); - } else { - console.log('[DB Migration] Schema is up to date.'); - resolve(); - } - }); - }); + checkPermissionsCompleted() { + const result = this.query( + 'SELECT value FROM system_settings WHERE key = ?', + ['permissions_completed'] + ); + return result.length > 0 && result[0].value === 'true'; } close() { if (this.db) { - this.db.close((err) => { - if (err) { - console.error('SQLite connection close failed:', err); - } else { - console.log('SQLite connection closed.'); - } - }); + try { + this.db.close(); + console.log('SQLite connection closed.'); + } catch (err) { + console.error('SQLite connection close failed:', err); + } this.db = null; } } - async query(sql, params = []) { - return new Promise((resolve, reject) => { - if (!this.db) { - return reject(new Error('Database not connected')); - } + query(sql, params = []) { + if (!this.db) { + throw new Error('Database not connected'); + } + try { if (sql.toUpperCase().startsWith('SELECT')) { - this.db.all(sql, params, (err, rows) => { - if (err) { - console.error('Query error:', err); - reject(err); - } else { - resolve(rows); - } - }); + return this.db.prepare(sql).all(params); } else { - this.db.run(sql, params, function(err) { - if (err) { - console.error('Query error:', err); - reject(err); - } else { - resolve({ changes: this.changes, lastID: this.lastID }); - } - }); + const result = this.db.prepare(sql).run(params); + return { changes: result.changes, lastID: result.lastID }; } - }); + } catch (err) { + console.error('Query error:', err); + throw err; + } } } diff --git a/src/electron/smoothMovementManager.js b/src/electron/smoothMovementManager.js new file mode 100644 index 0000000..a676c82 --- /dev/null +++ b/src/electron/smoothMovementManager.js @@ -0,0 +1,227 @@ +const { screen } = require('electron'); + +class SmoothMovementManager { + constructor(windowPool, getDisplayById, getCurrentDisplay, updateLayout) { + this.windowPool = windowPool; + this.getDisplayById = getDisplayById; + this.getCurrentDisplay = getCurrentDisplay; + this.updateLayout = updateLayout; + this.stepSize = 80; + this.animationDuration = 300; + this.headerPosition = { x: 0, y: 0 }; + this.isAnimating = false; + this.hiddenPosition = null; + this.lastVisiblePosition = null; + this.currentDisplayId = null; + this.animationFrameId = null; + } + + /** + * @param {BrowserWindow} win + * @returns {boolean} + */ + _isWindowValid(win) { + if (!win || win.isDestroyed()) { + if (this.isAnimating) { + console.warn('[MovementManager] Window destroyed mid-animation. Halting.'); + this.isAnimating = false; + if (this.animationFrameId) { + clearTimeout(this.animationFrameId); + this.animationFrameId = null; + } + } + return false; + } + return true; + } + + moveToDisplay(displayId) { + const header = this.windowPool.get('header'); + if (!this._isWindowValid(header) || !header.isVisible() || this.isAnimating) return; + + const targetDisplay = this.getDisplayById(displayId); + if (!targetDisplay) return; + + const currentBounds = header.getBounds(); + const currentDisplay = this.getCurrentDisplay(header); + + if (currentDisplay.id === targetDisplay.id) return; + + const relativeX = (currentBounds.x - currentDisplay.workArea.x) / currentDisplay.workAreaSize.width; + const relativeY = (currentBounds.y - currentDisplay.workArea.y) / currentDisplay.workAreaSize.height; + const targetX = targetDisplay.workArea.x + targetDisplay.workAreaSize.width * relativeX; + const targetY = targetDisplay.workArea.y + targetDisplay.workAreaSize.height * relativeY; + + const finalX = Math.max(targetDisplay.workArea.x, Math.min(targetDisplay.workArea.x + targetDisplay.workAreaSize.width - currentBounds.width, targetX)); + const finalY = Math.max(targetDisplay.workArea.y, Math.min(targetDisplay.workArea.y + targetDisplay.workAreaSize.height - currentBounds.height, targetY)); + + this.headerPosition = { x: currentBounds.x, y: currentBounds.y }; + this.animateToPosition(header, finalX, finalY); + this.currentDisplayId = targetDisplay.id; + } + + hideToEdge(edge, callback, { instant = false } = {}) { + const header = this.windowPool.get('header'); + if (!header || header.isDestroyed()) { + if (typeof callback === 'function') callback(); + return; + } + + const { x, y } = header.getBounds(); + this.lastVisiblePosition = { x, y }; + this.hiddenPosition = { edge }; + + if (instant) { + header.hide(); + if (typeof callback === 'function') callback(); + return; + } + + header.webContents.send('window-hide-animation'); + + setTimeout(() => { + if (!header.isDestroyed()) header.hide(); + if (typeof callback === 'function') callback(); + }, 5); + } + + showFromEdge(callback) { + const header = this.windowPool.get('header'); + if (!header || header.isDestroyed()) { + if (typeof callback === 'function') callback(); + return; + } + + // 숨기기 전에 κΈ°μ–΅ν•΄λ‘” μœ„μΉ˜ 볡ꡬ + if (this.lastVisiblePosition) { + header.setPosition( + this.lastVisiblePosition.x, + this.lastVisiblePosition.y, + false // animate: false + ); + } + + header.show(); + header.webContents.send('window-show-animation'); + + // λ‚΄λΆ€ μƒνƒœ μ΄ˆκΈ°ν™” + this.hiddenPosition = null; + this.lastVisiblePosition = null; + + if (typeof callback === 'function') callback(); + } + + moveStep(direction) { + const header = this.windowPool.get('header'); + if (!this._isWindowValid(header) || !header.isVisible() || this.isAnimating) return; + + const currentBounds = header.getBounds(); + this.headerPosition = { x: currentBounds.x, y: currentBounds.y }; + let targetX = this.headerPosition.x; + let targetY = this.headerPosition.y; + + switch (direction) { + case 'left': targetX -= this.stepSize; break; + case 'right': targetX += this.stepSize; break; + case 'up': targetY -= this.stepSize; break; + case 'down': targetY += this.stepSize; break; + default: return; + } + + const displays = screen.getAllDisplays(); + let validPosition = displays.some(d => ( + targetX >= d.workArea.x && targetX + currentBounds.width <= d.workArea.x + d.workArea.width && + targetY >= d.workArea.y && targetY + currentBounds.height <= d.workArea.y + d.workArea.height + )); + + if (!validPosition) { + const nearestDisplay = screen.getDisplayNearestPoint({ x: targetX, y: targetY }); + const { x, y, width, height } = nearestDisplay.workArea; + targetX = Math.max(x, Math.min(x + width - currentBounds.width, targetX)); + targetY = Math.max(y, Math.min(y + height - currentBounds.height, targetY)); + } + + if (targetX === this.headerPosition.x && targetY === this.headerPosition.y) return; + this.animateToPosition(header, targetX, targetY); + } + + animateToPosition(header, targetX, targetY) { + if (!this._isWindowValid(header)) return; + + this.isAnimating = true; + const startX = this.headerPosition.x; + const startY = this.headerPosition.y; + const startTime = Date.now(); + + if (!Number.isFinite(targetX) || !Number.isFinite(targetY) || !Number.isFinite(startX) || !Number.isFinite(startY)) { + this.isAnimating = false; + return; + } + + const animate = () => { + if (!this._isWindowValid(header)) return; + + const elapsed = Date.now() - startTime; + const progress = Math.min(elapsed / this.animationDuration, 1); + const eased = 1 - Math.pow(1 - progress, 3); + const currentX = startX + (targetX - startX) * eased; + const currentY = startY + (targetY - startY) * eased; + + if (!Number.isFinite(currentX) || !Number.isFinite(currentY)) { + this.isAnimating = false; + return; + } + + if (!this._isWindowValid(header)) return; + header.setPosition(Math.round(currentX), Math.round(currentY)); + + if (progress < 1) { + this.animationFrameId = setTimeout(animate, 8); + } else { + this.animationFrameId = null; + this.headerPosition = { x: targetX, y: targetY }; + if (Number.isFinite(targetX) && Number.isFinite(targetY)) { + if (!this._isWindowValid(header)) return; + header.setPosition(Math.round(targetX), Math.round(targetY)); + } + this.isAnimating = false; + this.updateLayout(); + } + }; + animate(); + } + + moveToEdge(direction) { + const header = this.windowPool.get('header'); + if (!this._isWindowValid(header) || !header.isVisible() || this.isAnimating) return; + + const display = this.getCurrentDisplay(header); + const { width, height } = display.workAreaSize; + const { x: workAreaX, y: workAreaY } = display.workArea; + const headerBounds = header.getBounds(); + const currentBounds = header.getBounds(); + let targetX = currentBounds.x; + let targetY = currentBounds.y; + + switch (direction) { + case 'left': targetX = workAreaX; break; + case 'right': targetX = workAreaX + width - headerBounds.width; break; + case 'up': targetY = workAreaY; break; + case 'down': targetY = workAreaY + height - headerBounds.height; break; + } + + this.headerPosition = { x: currentBounds.x, y: currentBounds.y }; + this.animateToPosition(header, targetX, targetY); + } + + destroy() { + if (this.animationFrameId) { + clearTimeout(this.animationFrameId); + this.animationFrameId = null; + } + this.isAnimating = false; + console.log('[Movement] Manager destroyed'); + } +} + +module.exports = SmoothMovementManager; \ No newline at end of file diff --git a/src/electron/windowLayoutManager.js b/src/electron/windowLayoutManager.js new file mode 100644 index 0000000..7893822 --- /dev/null +++ b/src/electron/windowLayoutManager.js @@ -0,0 +1,217 @@ +const { screen } = require('electron'); + +/** + * μ£Όμ–΄μ§„ 창이 ν˜„μž¬ μ–΄λŠ λ””μŠ€ν”Œλ ˆμ΄μ— 속해 μžˆλŠ”μ§€ λ°˜ν™˜ν•©λ‹ˆλ‹€. + * @param {BrowserWindow} window - 확인할 μ°½ 객체 + * @returns {Display} Electron의 Display 객체 + */ +function getCurrentDisplay(window) { + if (!window || window.isDestroyed()) return screen.getPrimaryDisplay(); + + const windowBounds = window.getBounds(); + const windowCenter = { + x: windowBounds.x + windowBounds.width / 2, + y: windowBounds.y + windowBounds.height / 2, + }; + + return screen.getDisplayNearestPoint(windowCenter); +} + +class WindowLayoutManager { + /** + * @param {Map} windowPool - 관리할 μ°½λ“€μ˜ λ§΅ + */ + constructor(windowPool) { + this.windowPool = windowPool; + this.isUpdating = false; + this.PADDING = 80; + } + + /** + * λͺ¨λ“  창의 λ ˆμ΄μ•„μ›ƒ μ—…λ°μ΄νŠΈλ₯Ό μš”μ²­ν•©λ‹ˆλ‹€. + * 쀑볡 싀행을 λ°©μ§€ν•˜κΈ° μœ„ν•΄ isUpdating ν”Œλž˜κ·Έλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. + */ + updateLayout() { + if (this.isUpdating) return; + this.isUpdating = true; + + setImmediate(() => { + this.positionWindows(); + this.isUpdating = false; + }); + } + + /** + * 헀더 창을 κΈ°μ€€μœΌλ‘œ λͺ¨λ“  κΈ°λŠ₯ μ°½λ“€μ˜ μœ„μΉ˜λ₯Ό κ³„μ‚°ν•˜κ³  λ°°μΉ˜ν•©λ‹ˆλ‹€. + */ + positionWindows() { + const header = this.windowPool.get('header'); + if (!header?.getBounds) return; + + const headerBounds = header.getBounds(); + const display = getCurrentDisplay(header); + const { width: screenWidth, height: screenHeight } = display.workAreaSize; + const { x: workAreaX, y: workAreaY } = display.workArea; + + const headerCenterX = headerBounds.x - workAreaX + headerBounds.width / 2; + const headerCenterY = headerBounds.y - workAreaY + headerBounds.height / 2; + + const relativeX = headerCenterX / screenWidth; + const relativeY = headerCenterY / screenHeight; + + const strategy = this.determineLayoutStrategy(headerBounds, screenWidth, screenHeight, relativeX, relativeY); + + this.positionFeatureWindows(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY); + this.positionSettingsWindow(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY); + } + + /** + * 헀더 창의 μœ„μΉ˜μ— 따라 κΈ°λŠ₯ 창듀을 λ°°μΉ˜ν•  졜적의 μ „λž΅μ„ κ²°μ •ν•©λ‹ˆλ‹€. + * @returns {{name: string, primary: string, secondary: string}} λ ˆμ΄μ•„μ›ƒ μ „λž΅ + */ + determineLayoutStrategy(headerBounds, screenWidth, screenHeight, relativeX, relativeY) { + const spaceBelow = screenHeight - (headerBounds.y + headerBounds.height); + const spaceAbove = headerBounds.y; + const spaceLeft = headerBounds.x; + const spaceRight = screenWidth - (headerBounds.x + headerBounds.width); + + if (spaceBelow >= 400) { + return { name: 'below', primary: 'below', secondary: relativeX < 0.5 ? 'right' : 'left' }; + } else if (spaceAbove >= 400) { + return { name: 'above', primary: 'above', secondary: relativeX < 0.5 ? 'right' : 'left' }; + } else if (relativeX < 0.3 && spaceRight >= 800) { + return { name: 'right-side', primary: 'right', secondary: spaceBelow > spaceAbove ? 'below' : 'above' }; + } else if (relativeX > 0.7 && spaceLeft >= 800) { + return { name: 'left-side', primary: 'left', secondary: spaceBelow > spaceAbove ? 'below' : 'above' }; + } else { + return { name: 'adaptive', primary: spaceBelow > spaceAbove ? 'below' : 'above', secondary: spaceRight > spaceLeft ? 'right' : 'left' }; + } + } + + /** + * 'ask'와 'listen' 창의 μœ„μΉ˜λ₯Ό μ‘°μ •ν•©λ‹ˆλ‹€. + */ + positionFeatureWindows(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY) { + const ask = this.windowPool.get('ask'); + const listen = this.windowPool.get('listen'); + const askVisible = ask && ask.isVisible() && !ask.isDestroyed(); + const listenVisible = listen && listen.isVisible() && !listen.isDestroyed(); + + if (!askVisible && !listenVisible) return; + + const PAD = 8; + const headerCenterXRel = headerBounds.x - workAreaX + headerBounds.width / 2; + let askBounds = askVisible ? ask.getBounds() : null; + let listenBounds = listenVisible ? listen.getBounds() : null; + + if (askVisible && listenVisible) { + const combinedWidth = listenBounds.width + PAD + askBounds.width; + let groupStartXRel = headerCenterXRel - combinedWidth / 2; + let listenXRel = groupStartXRel; + let askXRel = groupStartXRel + listenBounds.width + PAD; + + if (listenXRel < PAD) { + listenXRel = PAD; + askXRel = listenXRel + listenBounds.width + PAD; + } + if (askXRel + askBounds.width > screenWidth - PAD) { + askXRel = screenWidth - PAD - askBounds.width; + listenXRel = askXRel - listenBounds.width - PAD; + } + + let yRel = (strategy.primary === 'above') + ? headerBounds.y - workAreaY - Math.max(askBounds.height, listenBounds.height) - PAD + : headerBounds.y - workAreaY + headerBounds.height + PAD; + + listen.setBounds({ x: Math.round(listenXRel + workAreaX), y: Math.round(yRel + workAreaY), width: listenBounds.width, height: listenBounds.height }); + ask.setBounds({ x: Math.round(askXRel + workAreaX), y: Math.round(yRel + workAreaY), width: askBounds.width, height: askBounds.height }); + } else { + const win = askVisible ? ask : listen; + const winBounds = askVisible ? askBounds : listenBounds; + let xRel = headerCenterXRel - winBounds.width / 2; + let yRel = (strategy.primary === 'above') + ? headerBounds.y - workAreaY - winBounds.height - PAD + : headerBounds.y - workAreaY + headerBounds.height + PAD; + + xRel = Math.max(PAD, Math.min(screenWidth - winBounds.width - PAD, xRel)); + yRel = Math.max(PAD, Math.min(screenHeight - winBounds.height - PAD, yRel)); + + win.setBounds({ x: Math.round(xRel + workAreaX), y: Math.round(yRel + workAreaY), width: winBounds.width, height: winBounds.height }); + } + } + + /** + * 'settings' 창의 μœ„μΉ˜λ₯Ό μ‘°μ •ν•©λ‹ˆλ‹€. + */ + positionSettingsWindow(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY) { + const settings = this.windowPool.get('settings'); + if (!settings?.getBounds || !settings.isVisible()) return; + + if (settings.__lockedByButton) { + const headerDisplay = getCurrentDisplay(this.windowPool.get('header')); + const settingsDisplay = getCurrentDisplay(settings); + if (headerDisplay.id !== settingsDisplay.id) { + settings.__lockedByButton = false; + } else { + return; + } + } + + const settingsBounds = settings.getBounds(); + const PAD = 5; + const buttonPadding = 17; + let x = headerBounds.x + headerBounds.width - settingsBounds.width - buttonPadding; + let y = headerBounds.y + headerBounds.height + PAD; + + const otherVisibleWindows = []; + ['listen', 'ask'].forEach(name => { + const win = this.windowPool.get(name); + if (win && win.isVisible() && !win.isDestroyed()) { + otherVisibleWindows.push({ name, bounds: win.getBounds() }); + } + }); + + const settingsNewBounds = { x, y, width: settingsBounds.width, height: settingsBounds.height }; + let hasOverlap = otherVisibleWindows.some(otherWin => this.boundsOverlap(settingsNewBounds, otherWin.bounds)); + + if (hasOverlap) { + x = headerBounds.x + headerBounds.width + PAD; + y = headerBounds.y; + if (x + settingsBounds.width > screenWidth - 10) { + x = headerBounds.x - settingsBounds.width - PAD; + } + if (x < 10) { + x = headerBounds.x + headerBounds.width - settingsBounds.width - buttonPadding; + y = headerBounds.y - settingsBounds.height - PAD; + if (y < 10) { + x = headerBounds.x + headerBounds.width - settingsBounds.width; + y = headerBounds.y + headerBounds.height + PAD; + } + } + } + + x = Math.max(workAreaX + 10, Math.min(workAreaX + screenWidth - settingsBounds.width - 10, x)); + y = Math.max(workAreaY + 10, Math.min(workAreaY + screenHeight - settingsBounds.height - 10, y)); + + settings.setBounds({ x: Math.round(x), y: Math.round(y) }); + settings.moveTop(); + } + + /** + * 두 μ‚¬κ°ν˜• μ˜μ—­μ΄ κ²ΉμΉ˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. + * @param {Rectangle} bounds1 + * @param {Rectangle} bounds2 + * @returns {boolean} κ²ΉμΉ¨ μ—¬λΆ€ + */ + boundsOverlap(bounds1, bounds2) { + const margin = 10; + return !( + bounds1.x + bounds1.width + margin < bounds2.x || + bounds2.x + bounds2.width + margin < bounds1.x || + bounds1.y + bounds1.height + margin < bounds2.y || + bounds2.y + bounds2.height + margin < bounds1.y + ); + } +} + +module.exports = WindowLayoutManager; \ No newline at end of file diff --git a/src/electron/windowManager.js b/src/electron/windowManager.js index 9098d7c..ff85e16 100644 --- a/src/electron/windowManager.js +++ b/src/electron/windowManager.js @@ -1,21 +1,52 @@ const { BrowserWindow, globalShortcut, ipcMain, screen, app, shell, desktopCapturer } = require('electron'); +const WindowLayoutManager = require('./windowLayoutManager'); +const SmoothMovementManager = require('./smoothMovementManager'); const path = require('node:path'); const fs = require('node:fs'); const os = require('os'); const util = require('util'); const execFile = util.promisify(require('child_process').execFile); const sharp = require('sharp'); -const sqliteClient = require('../common/services/sqliteClient'); +const authService = require('../common/services/authService'); +const systemSettingsRepository = require('../common/repositories/systemSettings'); +const userRepository = require('../common/repositories/user'); const fetch = require('node-fetch'); +const Store = require('electron-store'); +const shortCutStore = new Store({ + name: 'user-preferences', + defaults: { + customKeybinds: {} + } +}); + +/* ────────────────[ GLASS BYPASS ]─────────────── */ +let liquidGlass; +const isLiquidGlassSupported = () => { + if (process.platform !== 'darwin') { + return false; + } + const majorVersion = parseInt(os.release().split('.')[0], 10); + // return majorVersion >= 25; // macOS 26+ (Darwin 25+) + return majorVersion >= 26; // See you soon! +}; +let shouldUseLiquidGlass = isLiquidGlassSupported(); +if (shouldUseLiquidGlass) { + try { + liquidGlass = require('electron-liquid-glass'); + } catch (e) { + console.warn('Could not load optional dependency "electron-liquid-glass". The feature will be disabled.'); + shouldUseLiquidGlass = false; + } +} +/* ────────────────[ GLASS BYPASS ]─────────────── */ -let currentFirebaseUser = null; let isContentProtectionOn = true; let currentDisplayId = null; let mouseEventsIgnored = false; let lastVisibleWindows = new Set(['header']); -const HEADER_HEIGHT = 60; -const DEFAULT_WINDOW_WIDTH = 345; +const HEADER_HEIGHT = 47; +const DEFAULT_WINDOW_WIDTH = 353; let currentHeaderState = 'apikey'; const windowPool = new Map(); @@ -26,47 +57,33 @@ let settingsHideTimer = null; let selectedCaptureSourceId = null; -const windowDefinitions = { - header: { - file: 'header.html', - options: { - /*…*/ - }, - allowedStates: ['apikey', 'app'], - }, - ask: { - file: 'ask.html', - options: { - /*…*/ - }, - allowedStates: ['app'], - }, - listen: { - file: 'assistant.html', - options: { - /*…*/ - }, - allowedStates: ['app'], - }, - settings: { - file: 'settings.html', - options: { - /*…*/ - }, - allowedStates: ['app'], - }, -}; +// let shortcutEditorWindow = null; +let layoutManager = null; +function updateLayout() { + if (layoutManager) { + layoutManager.updateLayout(); + } +} + +let movementManager = null; + const featureWindows = ['listen','ask','settings']; +// const featureWindows = ['listen','ask','settings','shortcut-settings']; +function isAllowed(name) { + if (name === 'header') return true; + return featureWindows.includes(name) && currentHeaderState === 'main'; +} -function createFeatureWindows(header) { - if (windowPool.has('listen')) return; +function createFeatureWindows(header, namesToCreate) { + // if (windowPool.has('listen')) return; const commonChildOptions = { parent: header, show: false, frame: false, transparent: true, + vibrancy: false, hasShadow: false, skipTaskbar: true, hiddenInMissionControl: true, @@ -74,36 +91,207 @@ function createFeatureWindows(header) { webPreferences: { nodeIntegration: true, contextIsolation: false }, }; - // listen - const listen = new BrowserWindow({ - ...commonChildOptions, width:400,height:300,minWidth:400,maxWidth:400, - minHeight:200,maxHeight:700, - }); - listen.setContentProtection(isContentProtectionOn); - listen.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true}); - listen.loadFile(path.join(__dirname,'../app/content.html'),{query:{view:'listen'}}); - windowPool.set('listen', listen); + const createFeatureWindow = (name) => { + if (windowPool.has(name)) return; + + switch (name) { + case 'listen': { + const listen = new BrowserWindow({ + ...commonChildOptions, width:400,minWidth:400,maxWidth:400, + maxHeight:700, + }); + listen.setContentProtection(isContentProtectionOn); + listen.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true}); + if (process.platform === 'darwin') { + listen.setWindowButtonVisibility(false); + } + const listenLoadOptions = { query: { view: 'listen' } }; + if (!shouldUseLiquidGlass) { + listen.loadFile(path.join(__dirname, '../app/content.html'), listenLoadOptions); + } + else { + listenLoadOptions.query.glass = 'true'; + listen.loadFile(path.join(__dirname, '../app/content.html'), listenLoadOptions); + listen.webContents.once('did-finish-load', () => { + const viewId = liquidGlass.addView(listen.getNativeWindowHandle(), { + cornerRadius: 12, + tintColor: '#FF00001A', // Red tint + opaque: false, + }); + if (viewId !== -1) { + liquidGlass.unstable_setVariant(viewId, 2); + // liquidGlass.unstable_setScrim(viewId, 1); + // liquidGlass.unstable_setSubdued(viewId, 1); + } + }); + } + windowPool.set('listen', listen); + break; + } - // ask - const ask = new BrowserWindow({ ...commonChildOptions, width:600, height:350 }); - ask.setContentProtection(isContentProtectionOn); - ask.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true}); - ask.loadFile(path.join(__dirname,'../app/content.html'),{query:{view:'ask'}}); - ask.on('blur',()=>ask.webContents.send('window-blur')); - - // Open DevTools in development - if (!app.isPackaged) { - ask.webContents.openDevTools({ mode: 'detach' }); + // ask + case 'ask': { + const ask = new BrowserWindow({ ...commonChildOptions, width:600 }); + ask.setContentProtection(isContentProtectionOn); + ask.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true}); + if (process.platform === 'darwin') { + ask.setWindowButtonVisibility(false); + } + const askLoadOptions = { query: { view: 'ask' } }; + if (!shouldUseLiquidGlass) { + ask.loadFile(path.join(__dirname, '../app/content.html'), askLoadOptions); + } + else { + askLoadOptions.query.glass = 'true'; + ask.loadFile(path.join(__dirname, '../app/content.html'), askLoadOptions); + ask.webContents.once('did-finish-load', () => { + const viewId = liquidGlass.addView(ask.getNativeWindowHandle(), { + cornerRadius: 12, + tintColor: '#FF00001A', // Red tint + opaque: false, + }); + if (viewId !== -1) { + liquidGlass.unstable_setVariant(viewId, 2); + // liquidGlass.unstable_setScrim(viewId, 1); + // liquidGlass.unstable_setSubdued(viewId, 1); + } + }); + } + + ask.on('blur',()=>ask.webContents.send('window-blur')); + + // Open DevTools in development + if (!app.isPackaged) { + ask.webContents.openDevTools({ mode: 'detach' }); + } + windowPool.set('ask', ask); + break; + } + + // settings + case 'settings': { + const settings = new BrowserWindow({ ...commonChildOptions, width:240, maxHeight:400, parent:undefined }); + settings.setContentProtection(isContentProtectionOn); + settings.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true}); + if (process.platform === 'darwin') { + settings.setWindowButtonVisibility(false); + } + const settingsLoadOptions = { query: { view: 'settings' } }; + if (!shouldUseLiquidGlass) { + settings.loadFile(path.join(__dirname,'../app/content.html'), settingsLoadOptions) + .catch(console.error); + } + else { + settingsLoadOptions.query.glass = 'true'; + settings.loadFile(path.join(__dirname,'../app/content.html'), settingsLoadOptions) + .catch(console.error); + settings.webContents.once('did-finish-load', () => { + const viewId = liquidGlass.addView(settings.getNativeWindowHandle(), { + cornerRadius: 12, + tintColor: '#FF00001A', // Red tint + opaque: false, + }); + if (viewId !== -1) { + liquidGlass.unstable_setVariant(viewId, 2); + // liquidGlass.unstable_setScrim(viewId, 1); + // liquidGlass.unstable_setSubdued(viewId, 1); + } + }); + } + windowPool.set('settings', settings); + break; + } + + case 'shortcut-settings': { + const shortcutEditor = new BrowserWindow({ + ...commonChildOptions, + width: 420, + height: 720, + modal: false, + parent: undefined, + alwaysOnTop: true, + titleBarOverlay: false, + }); + + if (process.platform === 'darwin') { + shortcutEditor.setAlwaysOnTop(true, 'screen-saver'); + } else { + shortcutEditor.setAlwaysOnTop(true); + } + + /* ──────────[ β‘  λ‹€λ₯Έ μ°½ 클릭 차단 ]────────── */ + const disableClicks = () => { + for (const [name, win] of windowPool) { + if (win !== shortcutEditor && !win.isDestroyed()) { + win.setIgnoreMouseEvents(true, { forward: true }); + } + } + }; + const restoreClicks = () => { + for (const [, win] of windowPool) { + if (!win.isDestroyed()) win.setIgnoreMouseEvents(false); + } + }; + + const header = windowPool.get('header'); + if (header && !header.isDestroyed()) { + const { x, y, width } = header.getBounds(); + shortcutEditor.setBounds({ x, y, width }); + } + + shortcutEditor.once('ready-to-show', () => { + disableClicks(); + shortcutEditor.show(); + }); + + const loadOptions = { query: { view: 'shortcut-settings' } }; + if (!shouldUseLiquidGlass) { + shortcutEditor.loadFile(path.join(__dirname, '../app/content.html'), loadOptions); + } else { + loadOptions.query.glass = 'true'; + shortcutEditor.loadFile(path.join(__dirname, '../app/content.html'), loadOptions); + shortcutEditor.webContents.once('did-finish-load', () => { + const viewId = liquidGlass.addView(shortcutEditor.getNativeWindowHandle(), { + cornerRadius: 12, tintColor: '#FF00001A', opaque: false, + }); + if (viewId !== -1) { + liquidGlass.unstable_setVariant(viewId, 2); + } + }); + } + + shortcutEditor.on('closed', () => { + restoreClicks(); + windowPool.delete('shortcut-settings'); + console.log('[Shortcuts] Re-enabled after editing.'); + loadAndRegisterShortcuts(movementManager); + }); + + shortcutEditor.webContents.once('dom-ready', async () => { + const savedKeybinds = shortCutStore.get('customKeybinds', {}); + const defaultKeybinds = getDefaultKeybinds(); + const keybinds = { ...defaultKeybinds, ...savedKeybinds }; + shortcutEditor.webContents.send('load-shortcuts', keybinds); + }); + + if (!app.isPackaged) { + shortcutEditor.webContents.openDevTools({ mode: 'detach' }); + } + windowPool.set('shortcut-settings', shortcutEditor); + break; + } + } + }; + + if (Array.isArray(namesToCreate)) { + namesToCreate.forEach(name => createFeatureWindow(name)); + } else if (typeof namesToCreate === 'string') { + createFeatureWindow(namesToCreate); + } else { + createFeatureWindow('listen'); + createFeatureWindow('ask'); + createFeatureWindow('settings'); } - windowPool.set('ask', ask); - - // settings - const settings = new BrowserWindow({ ...commonChildOptions, width:240, height:450, parent:undefined }); - settings.setContentProtection(isContentProtectionOn); - settings.setVisibleOnAllWorkspaces(true,{visibleOnFullScreen:true}); - settings.loadFile(path.join(__dirname,'../app/content.html'),{query:{view:'customize'}}) - .catch(console.error); - windowPool.set('settings', settings); } function destroyFeatureWindows() { @@ -118,10 +306,7 @@ function destroyFeatureWindows() { }); } -function isAllowed(name) { - const def = windowDefinitions[name]; - return def && def.allowedStates.includes(currentHeaderState); -} + function getCurrentDisplay(window) { if (!window || window.isDestroyed()) return screen.getPrimaryDisplay(); @@ -140,713 +325,9 @@ function getDisplayById(displayId) { return displays.find(d => d.id === displayId) || screen.getPrimaryDisplay(); } -class WindowLayoutManager { - constructor() { - this.isUpdating = false; - this.PADDING = 80; - } - updateLayout() { - if (this.isUpdating) return; - this.isUpdating = true; - setImmediate(() => { - this.positionWindows(); - this.isUpdating = false; - }); - } - - positionWindows() { - const header = windowPool.get('header'); - if (!header?.getBounds) return; - - const headerBounds = header.getBounds(); - const display = getCurrentDisplay(header); - const { width: screenWidth, height: screenHeight } = display.workAreaSize; - const { x: workAreaX, y: workAreaY } = display.workArea; - - const headerCenterX = headerBounds.x - workAreaX + headerBounds.width / 2; - const headerCenterY = headerBounds.y - workAreaY + headerBounds.height / 2; - - const relativeX = headerCenterX / screenWidth; - const relativeY = headerCenterY / screenHeight; - - const strategy = this.determineLayoutStrategy(headerBounds, screenWidth, screenHeight, relativeX, relativeY); - - this.positionFeatureWindows(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY); - this.positionSettingsWindow(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY); - } - - determineLayoutStrategy(headerBounds, screenWidth, screenHeight, relativeX, relativeY) { - const spaceBelow = screenHeight - (headerBounds.y + headerBounds.height); - const spaceAbove = headerBounds.y; - const spaceLeft = headerBounds.x; - const spaceRight = screenWidth - (headerBounds.x + headerBounds.width); - - const spaces = { - below: spaceBelow, - above: spaceAbove, - left: spaceLeft, - right: spaceRight, - }; - - if (spaceBelow >= 400) { - return { - name: 'below', - primary: 'below', - secondary: relativeX < 0.5 ? 'right' : 'left', - }; - } else if (spaceAbove >= 400) { - return { - name: 'above', - primary: 'above', - secondary: relativeX < 0.5 ? 'right' : 'left', - }; - } else if (relativeX < 0.3 && spaceRight >= 800) { - return { - name: 'right-side', - primary: 'right', - secondary: spaceBelow > spaceAbove ? 'below' : 'above', - }; - } else if (relativeX > 0.7 && spaceLeft >= 800) { - return { - name: 'left-side', - primary: 'left', - secondary: spaceBelow > spaceAbove ? 'below' : 'above', - }; - } else { - return { - name: 'adaptive', - primary: spaceBelow > spaceAbove ? 'below' : 'above', - secondary: spaceRight > spaceLeft ? 'right' : 'left', - }; - } - } - - positionFeatureWindows(headerBounds, strategy, screenWidth, screenHeight, workAreaX, workAreaY) { - const ask = windowPool.get('ask'); - const listen = windowPool.get('listen'); - const askVisible = ask && ask.isVisible() && !ask.isDestroyed(); - const listenVisible = listen && listen.isVisible() && !listen.isDestroyed(); - - if (!askVisible && !listenVisible) return; - - const PAD = 8; - - /* β‘  헀더 쀑심 Xλ₯Ό β€œλ””μŠ€ν”Œλ ˆμ΄ κΈ°μ€€ μƒλŒ€μ’Œν‘œβ€λ‘œ λ³€ν™˜ */ - const headerCenterXRel = headerBounds.x - workAreaX + headerBounds.width / 2; - - let askBounds = askVisible ? ask.getBounds() : null; - let listenBounds = listenVisible ? listen.getBounds() : null; - - /* ------------------------------------------------- */ - /* 두 μ°½ λͺ¨λ‘ λ³΄μ΄λŠ” 경우 */ - /* ------------------------------------------------- */ - if (askVisible && listenVisible) { - const combinedWidth = listenBounds.width + PAD + askBounds.width; - - /* β‘‘ λͺ¨λ“  X μ’Œν‘œλ₯Ό μƒλŒ€μ’Œν‘œλ‘œ 계산 */ - let groupStartXRel = headerCenterXRel - combinedWidth / 2; - let listenXRel = groupStartXRel; - let askXRel = groupStartXRel + listenBounds.width + PAD; - - /* 쒌우 ν™”λ©΄ μ—¬λ°± ν΄λž¨ν”„ – μ—­μ‹œ μƒλŒ€μ’Œν‘œλ‘œ */ - if (listenXRel < PAD) { - listenXRel = PAD; - askXRel = listenXRel + listenBounds.width + PAD; - } - if (askXRel + askBounds.width > screenWidth - PAD) { - askXRel = screenWidth - PAD - askBounds.width; - listenXRel = askXRel - listenBounds.width - PAD; - } - - /* Y μ’Œν‘œλŠ” 이미 μƒλŒ€κ°’μœΌλ‘œ 계산돼 있음 */ - let yRel; - switch (strategy.primary) { - case 'below': - yRel = headerBounds.y - workAreaY + headerBounds.height + PAD; - break; - case 'above': - yRel = headerBounds.y - workAreaY - Math.max(askBounds.height, listenBounds.height) - PAD; - break; - default: - yRel = headerBounds.y - workAreaY + headerBounds.height + PAD; - break; - } - - /* β‘’ setBounds 직전에 workAreaX/Yλ₯Ό 더해 μ ˆλŒ€μ’Œν‘œλ‘œ λ³€ν™˜ */ - listen.setBounds({ - x: Math.round(listenXRel + workAreaX), - y: Math.round(yRel + workAreaY), - width: listenBounds.width, - height: listenBounds.height, - }); - ask.setBounds({ - x: Math.round(askXRel + workAreaX), - y: Math.round(yRel + workAreaY), - width: askBounds.width, - height: askBounds.height, - }); - - /* ------------------------------------------------- */ - /* ν•˜λ‚˜λ§Œ λ³΄μ΄λŠ” 경우 */ - /* ------------------------------------------------- */ - } else { - const win = askVisible ? ask : listen; - const winBounds = askVisible ? askBounds : listenBounds; - - /* X, Y λ‘˜ λ‹€ μƒλŒ€μ’Œν‘œλ‘œ 계산 */ - let xRel = headerCenterXRel - winBounds.width / 2; - let yRel; - switch (strategy.primary) { - case 'below': - yRel = headerBounds.y - workAreaY + headerBounds.height + PAD; - break; - case 'above': - yRel = headerBounds.y - workAreaY - winBounds.height - PAD; - break; - default: - yRel = headerBounds.y - workAreaY + headerBounds.height + PAD; - break; - } - - /* ν™”λ©΄ 경계 ν΄λž¨ν”„ */ - xRel = Math.max(PAD, Math.min(screenWidth - winBounds.width - PAD, xRel)); - yRel = Math.max(PAD, Math.min(screenHeight - winBounds.height - PAD, yRel)); - - /* μ ˆλŒ€μ’Œν‘œλ‘œ λ³€ν™˜ ν›„ 배치 */ - win.setBounds({ - x: Math.round(xRel + workAreaX), - y: Math.round(yRel + workAreaY), - width: winBounds.width, - height: winBounds.height, - }); - } - } - - positionSettingsWindow(headerBounds, strategy, screenWidth, screenHeight) { - const settings = windowPool.get('settings'); - if (!settings?.getBounds || !settings.isVisible()) return; - - // if (settings.__lockedByButton) return; - if (settings.__lockedByButton) { - const headerDisplay = getCurrentDisplay(windowPool.get('header')); - const settingsDisplay = getCurrentDisplay(settings); - if (headerDisplay.id !== settingsDisplay.id) { - settings.__lockedByButton = false; - } else { - return; // 같은 화면이면 κ·ΈλŒ€λ‘œ λ‘”λ‹€ - } - } - - const settingsBounds = settings.getBounds(); - const PAD = 5; - - const buttonPadding = 17; - let x = headerBounds.x + headerBounds.width - settingsBounds.width - buttonPadding; - let y = headerBounds.y + headerBounds.height + PAD; - - const otherVisibleWindows = []; - ['listen', 'ask'].forEach(name => { - const win = windowPool.get(name); - if (win && win.isVisible() && !win.isDestroyed()) { - otherVisibleWindows.push({ - name, - bounds: win.getBounds(), - }); - } - }); - - const settingsNewBounds = { x, y, width: settingsBounds.width, height: settingsBounds.height }; - let hasOverlap = false; - - for (const otherWin of otherVisibleWindows) { - if (this.boundsOverlap(settingsNewBounds, otherWin.bounds)) { - hasOverlap = true; - break; - } - } - - if (hasOverlap) { - x = headerBounds.x + headerBounds.width + PAD; - y = headerBounds.y; - settingsNewBounds.x = x; - settingsNewBounds.y = y; - - if (x + settingsBounds.width > screenWidth - 10) { - x = headerBounds.x - settingsBounds.width - PAD; - settingsNewBounds.x = x; - } - - if (x < 10) { - x = headerBounds.x + headerBounds.width - settingsBounds.width - buttonPadding; - y = headerBounds.y - settingsBounds.height - PAD; - settingsNewBounds.x = x; - settingsNewBounds.y = y; - - if (y < 10) { - x = headerBounds.x + headerBounds.width - settingsBounds.width; - y = headerBounds.y + headerBounds.height + PAD; - } - } - } - - x = Math.max(10, Math.min(screenWidth - settingsBounds.width - 10, x)); - y = Math.max(10, Math.min(screenHeight - settingsBounds.height - 10, y)); - - settings.setBounds({ x, y }); - settings.moveTop(); - - // console.log(`[Layout] Settings positioned at (${x}, ${y}) ${hasOverlap ? '(adjusted for overlap)' : '(default position)'}`); - } - - boundsOverlap(bounds1, bounds2) { - const margin = 10; - return !( - bounds1.x + bounds1.width + margin < bounds2.x || - bounds2.x + bounds2.width + margin < bounds1.x || - bounds1.y + bounds1.height + margin < bounds2.y || - bounds2.y + bounds2.height + margin < bounds1.y - ); - } - - isWindowVisible(windowName) { - const window = windowPool.get(windowName); - return window && !window.isDestroyed() && window.isVisible(); - } - - destroy() {} -} - -class SmoothMovementManager { - constructor() { - this.stepSize = 80; - this.animationDuration = 300; - this.headerPosition = { x: 0, y: 0 }; - this.isAnimating = false; - this.hiddenPosition = null; - this.lastVisiblePosition = null; - this.currentDisplayId = null; - } - - moveToDisplay(displayId) { - const header = windowPool.get('header'); - if (!header || !header.isVisible() || this.isAnimating) return; - - const targetDisplay = getDisplayById(displayId); - if (!targetDisplay) return; - - const currentBounds = header.getBounds(); - const currentDisplay = getCurrentDisplay(header); - - if (currentDisplay.id === targetDisplay.id) { - console.log('[Movement] Already on target display'); - return; - } - - const relativeX = (currentBounds.x - currentDisplay.workArea.x) / currentDisplay.workAreaSize.width; - const relativeY = (currentBounds.y - currentDisplay.workArea.y) / currentDisplay.workAreaSize.height; - - const targetX = targetDisplay.workArea.x + targetDisplay.workAreaSize.width * relativeX; - const targetY = targetDisplay.workArea.y + targetDisplay.workAreaSize.height * relativeY; - - const finalX = Math.max( - targetDisplay.workArea.x, - Math.min(targetDisplay.workArea.x + targetDisplay.workAreaSize.width - currentBounds.width, targetX) - ); - const finalY = Math.max( - targetDisplay.workArea.y, - Math.min(targetDisplay.workArea.y + targetDisplay.workAreaSize.height - currentBounds.height, targetY) - ); - - this.headerPosition = { x: currentBounds.x, y: currentBounds.y }; - this.animateToPosition(header, finalX, finalY); - - this.currentDisplayId = targetDisplay.id; - } - - hideToEdge(edge, callback) { - const header = windowPool.get('header'); - if (!header || !header.isVisible() || this.isAnimating) return; - - console.log(`[Movement] Hiding to ${edge} edge`); - - const currentBounds = header.getBounds(); - this.lastVisiblePosition = { x: currentBounds.x, y: currentBounds.y }; - this.headerPosition = { x: currentBounds.x, y: currentBounds.y }; - - const display = getCurrentDisplay(header); - const { width: screenWidth, height: screenHeight } = display.workAreaSize; - const { x: workAreaX, y: workAreaY } = display.workArea; - const headerBounds = header.getBounds(); - - let targetX = this.headerPosition.x; - let targetY = this.headerPosition.y; - - switch (edge) { - case 'top': - targetY = workAreaY - headerBounds.height - 20; - break; - case 'bottom': - targetY = workAreaY + screenHeight + 20; - break; - case 'left': - targetX = workAreaX - headerBounds.width - 20; - break; - case 'right': - targetX = workAreaX + screenWidth + 20; - break; - } - - this.hiddenPosition = { x: targetX, y: targetY, edge }; - - this.isAnimating = true; - const startX = this.headerPosition.x; - const startY = this.headerPosition.y; - const duration = 400; - const startTime = Date.now(); - - const animate = () => { - if (!header || typeof header.setPosition !== 'function' || header.isDestroyed()) { - this.isAnimating = false; - return; - } - - const elapsed = Date.now() - startTime; - const progress = Math.min(elapsed / duration, 1); - const eased = progress * progress * progress; - - const currentX = startX + (targetX - startX) * eased; - const currentY = startY + (targetY - startY) * eased; - - // Validate computed positions before using - if (!Number.isFinite(currentX) || !Number.isFinite(currentY)) { - console.error('[Movement] Invalid animation values for hide:', { - currentX, currentY, progress, eased, startX, startY, targetX, targetY - }); - this.isAnimating = false; - return; - } - - // Safely call setPosition - try { - header.setPosition(Math.round(currentX), Math.round(currentY)); - } catch (err) { - console.error('[Movement] Failed to set position:', err); - this.isAnimating = false; - return; - } - - if (progress < 1) { - setTimeout(animate, 8); - } else { - this.headerPosition = { x: targetX, y: targetY }; - - if (Number.isFinite(targetX) && Number.isFinite(targetY)) { - try { - header.setPosition(Math.round(targetX), Math.round(targetY)); - } catch (err) { - console.error('[Movement] Failed to set final position:', err); - } - } - - this.isAnimating = false; - - if (typeof callback === 'function') { - try { - callback(); - } catch (err) { - console.error('[Movement] Callback error:', err); - } - } - - console.log(`[Movement] Hide to ${edge} completed`); - } - }; - - animate(); - } - - showFromEdge(callback) { - const header = windowPool.get('header'); - if (!header || this.isAnimating || !this.hiddenPosition || !this.lastVisiblePosition) return; - - console.log(`[Movement] Showing from ${this.hiddenPosition.edge} edge`); - - header.setPosition(this.hiddenPosition.x, this.hiddenPosition.y); - this.headerPosition = { x: this.hiddenPosition.x, y: this.hiddenPosition.y }; - - const targetX = this.lastVisiblePosition.x; - const targetY = this.lastVisiblePosition.y; - - this.isAnimating = true; - const startX = this.headerPosition.x; - const startY = this.headerPosition.y; - const duration = 500; - const startTime = Date.now(); - - const animate = () => { - if (!header || header.isDestroyed()) { - this.isAnimating = false; - return; - } - - const elapsed = Date.now() - startTime; - const progress = Math.min(elapsed / duration, 1); - - const c1 = 1.70158; - const c3 = c1 + 1; - const eased = 1 + c3 * Math.pow(progress - 1, 3) + c1 * Math.pow(progress - 1, 2); - - const currentX = startX + (targetX - startX) * eased; - const currentY = startY + (targetY - startY) * eased; - - if (!Number.isFinite(currentX) || !Number.isFinite(currentY)) { - console.error('[Movement] Invalid animation values for show:', { currentX, currentY, progress, eased }); - this.isAnimating = false; - return; - } - - header.setPosition(Math.round(currentX), Math.round(currentY)); - - if (progress < 1) { - setTimeout(animate, 8); - } else { - this.headerPosition = { x: targetX, y: targetY }; - this.headerPosition = { x: targetX, y: targetY }; - if (Number.isFinite(targetX) && Number.isFinite(targetY)) { - header.setPosition(Math.round(targetX), Math.round(targetY)); - } - this.isAnimating = false; - - this.hiddenPosition = null; - this.lastVisiblePosition = null; - - if (callback) callback(); - - console.log(`[Movement] Show from edge completed`); - } - }; - - animate(); - } - - moveStep(direction) { - const header = windowPool.get('header'); - if (!header || !header.isVisible() || this.isAnimating) return; - - console.log(`[Movement] Step ${direction}`); - - const currentBounds = header.getBounds(); - this.headerPosition = { x: currentBounds.x, y: currentBounds.y }; - - let targetX = this.headerPosition.x; - let targetY = this.headerPosition.y; - - switch (direction) { - case 'left': - targetX -= this.stepSize; - break; - case 'right': - targetX += this.stepSize; - break; - case 'up': - targetY -= this.stepSize; - break; - case 'down': - targetY += this.stepSize; - break; - default: - return; - } - - const displays = screen.getAllDisplays(); - let validPosition = false; - - for (const display of displays) { - const { x, y, width, height } = display.workArea; - const headerBounds = header.getBounds(); - - if (targetX >= x && targetX + headerBounds.width <= x + width && targetY >= y && targetY + headerBounds.height <= y + height) { - validPosition = true; - break; - } - } - - if (!validPosition) { - const nearestDisplay = screen.getDisplayNearestPoint({ x: targetX, y: targetY }); - const { x, y, width, height } = nearestDisplay.workArea; - const headerBounds = header.getBounds(); - - targetX = Math.max(x, Math.min(x + width - headerBounds.width, targetX)); - targetY = Math.max(y, Math.min(y + height - headerBounds.height, targetY)); - } - - if (targetX === this.headerPosition.x && targetY === this.headerPosition.y) { - console.log(`[Movement] Already at boundary for ${direction}`); - return; - } - - this.animateToPosition(header, targetX, targetY); - } - - animateToPosition(header, targetX, targetY) { - this.isAnimating = true; - - const startX = this.headerPosition.x; - const startY = this.headerPosition.y; - const startTime = Date.now(); - - if (!Number.isFinite(targetX) || !Number.isFinite(targetY) || !Number.isFinite(startX) || !Number.isFinite(startY)) { - console.error('[Movement] Invalid position values:', { startX, startY, targetX, targetY }); - this.isAnimating = false; - return; - } - - const animate = () => { - if (!header || header.isDestroyed()) { - this.isAnimating = false; - return; - } - - const elapsed = Date.now() - startTime; - const progress = Math.min(elapsed / this.animationDuration, 1); - - const eased = 1 - Math.pow(1 - progress, 3); - - const currentX = startX + (targetX - startX) * eased; - const currentY = startY + (targetY - startY) * eased; - - if (!Number.isFinite(currentX) || !Number.isFinite(currentY)) { - console.error('[Movement] Invalid animation values:', { currentX, currentY, progress, eased }); - this.isAnimating = false; - return; - } - - header.setPosition(Math.round(currentX), Math.round(currentY)); - - if (progress < 1) { - setTimeout(animate, 8); - } else { - this.headerPosition = { x: targetX, y: targetY }; - if (Number.isFinite(targetX) && Number.isFinite(targetY)) { - header.setPosition(Math.round(targetX), Math.round(targetY)); - } else { - console.warn('[Movement] Final position invalid, skip setPosition:', { targetX, targetY }); - } - this.isAnimating = false; - - updateLayout(); - - console.log(`[Movement] Step completed to (${targetX}, ${targetY})`); - } - }; - - animate(); - } - - moveToEdge(direction) { - const header = windowPool.get('header'); - if (!header || !header.isVisible() || this.isAnimating) return; - - console.log(`[Movement] Move to edge: ${direction}`); - - const display = getCurrentDisplay(header); - const { width, height } = display.workAreaSize; - const { x: workAreaX, y: workAreaY } = display.workArea; - const headerBounds = header.getBounds(); - - const currentBounds = header.getBounds(); - let targetX = currentBounds.x; - let targetY = currentBounds.y; - - switch (direction) { - case 'left': - targetX = workAreaX; - break; - case 'right': - targetX = workAreaX + width - headerBounds.width; - break; - case 'up': - targetY = workAreaY; - break; - case 'down': - targetY = workAreaY + height - headerBounds.height; - break; - } - - this.headerPosition = { x: currentBounds.x, y: currentBounds.y }; - - this.isAnimating = true; - const startX = this.headerPosition.x; - const startY = this.headerPosition.y; - const duration = 400; - const startTime = Date.now(); // 이 쀄을 animate ν•¨μˆ˜ μ •μ˜ μ „μœΌλ‘œ 이동 - - if (!Number.isFinite(targetX) || !Number.isFinite(targetY) || !Number.isFinite(startX) || !Number.isFinite(startY)) { - console.error('[Movement] Invalid edge position values:', { startX, startY, targetX, targetY }); - this.isAnimating = false; - return; - } - - const animate = () => { - if (!header || header.isDestroyed()) { - this.isAnimating = false; - return; - } - - const elapsed = Date.now() - startTime; - const progress = Math.min(elapsed / duration, 1); - - const eased = 1 - Math.pow(1 - progress, 4); - - const currentX = startX + (targetX - startX) * eased; - const currentY = startY + (targetY - startY) * eased; - - if (!Number.isFinite(currentX) || !Number.isFinite(currentY)) { - console.error('[Movement] Invalid edge animation values:', { currentX, currentY, progress, eased }); - this.isAnimating = false; - return; - } - - header.setPosition(Math.round(currentX), Math.round(currentY)); - - if (progress < 1) { - setTimeout(animate, 8); - } else { - if (Number.isFinite(targetX) && Number.isFinite(targetY)) { - header.setPosition(Math.round(targetX), Math.round(targetY)); - } - this.headerPosition = { x: targetX, y: targetY }; - this.isAnimating = false; - - updateLayout(); - - console.log(`[Movement] Edge movement completed: ${direction}`); - } - }; - - animate(); - } - - handleKeyPress(direction) {} - - handleKeyRelease(direction) {} - - forceStopMovement() { - this.isAnimating = false; - } - - destroy() { - this.isAnimating = false; - console.log('[Movement] Destroyed'); - } -} - -const layoutManager = new WindowLayoutManager(); -let movementManager = null; - -function toggleAllWindowsVisibility() { +function toggleAllWindowsVisibility(movementManager) { const header = windowPool.get('header'); if (!header) return; @@ -878,12 +359,13 @@ function toggleAllWindowsVisibility() { if (win.isVisible()) { lastVisibleWindows.add(name); if (name !== 'header') { - win.webContents.send('window-hide-animation'); - setTimeout(() => { - if (!win.isDestroyed()) { - win.hide(); - } - }, 200); + // win.webContents.send('window-hide-animation'); + // setTimeout(() => { + // if (!win.isDestroyed()) { + // win.hide(); + // } + // }, 200); + win.hide(); } } }); @@ -893,7 +375,7 @@ function toggleAllWindowsVisibility() { movementManager.hideToEdge(nearestEdge, () => { header.hide(); console.log('[Visibility] Smart hide completed'); - }); + }, { instant: true }); } else { console.log('[Visibility] Smart showing from hidden position'); console.log('[Visibility] Restoring windows:', Array.from(lastVisibleWindows)); @@ -918,21 +400,6 @@ function toggleAllWindowsVisibility() { } } -function ensureDataDirectories() { - const homeDir = os.homedir(); - const pickleGlassDir = path.join(homeDir, '.pickle-glass'); - const dataDir = path.join(pickleGlassDir, 'data'); - const imageDir = path.join(dataDir, 'image'); - const audioDir = path.join(dataDir, 'audio'); - - [pickleGlassDir, dataDir, imageDir, audioDir].forEach(dir => { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - }); - - return { imageDir, audioDir }; -} function createWindows() { const primaryDisplay = screen.getPrimaryDisplay(); @@ -940,8 +407,8 @@ function createWindows() { const initialX = Math.round((screenWidth - DEFAULT_WINDOW_WIDTH) / 2); const initialY = workAreaY + 21; - movementManager = new SmoothMovementManager(); - + movementManager = new SmoothMovementManager(windowPool, getDisplayById, getCurrentDisplay, updateLayout); + const header = new BrowserWindow({ width: DEFAULT_WINDOW_WIDTH, height: HEADER_HEIGHT, @@ -949,6 +416,7 @@ function createWindows() { y: initialY, frame: false, transparent: true, + vibrancy: false, alwaysOnTop: true, skipTaskbar: true, hiddenInMissionControl: true, @@ -962,23 +430,45 @@ function createWindows() { webSecurity: false, }, }); - - windowPool.set('header', header); - - if (currentHeaderState === 'app') { - createFeatureWindows(header); + if (process.platform === 'darwin') { + header.setWindowButtonVisibility(false); + } + const headerLoadOptions = {}; + if (!shouldUseLiquidGlass) { + header.loadFile(path.join(__dirname, '../app/header.html'), headerLoadOptions); + } + else { + headerLoadOptions.query = { glass: 'true' }; + header.loadFile(path.join(__dirname, '../app/header.html'), headerLoadOptions); + header.webContents.once('did-finish-load', () => { + const viewId = liquidGlass.addView(header.getNativeWindowHandle(), { + cornerRadius: 12, + tintColor: '#FF00001A', // Red tint + opaque: false, + }); + if (viewId !== -1) { + liquidGlass.unstable_setVariant(viewId, 2); + // liquidGlass.unstable_setScrim(viewId, 1); + // liquidGlass.unstable_setSubdued(viewId, 1); + } + }); } - - windowPool.set('header', header); + layoutManager = new WindowLayoutManager(windowPool); - if (currentHeaderState === 'app') { - createFeatureWindows(header); + header.webContents.once('dom-ready', () => { + loadAndRegisterShortcuts(movementManager); + }); + + setupIpcHandlers(movementManager); + + if (currentHeaderState === 'main') { + createFeatureWindows(header, ['listen', 'ask', 'settings', 'shortcut-settings']); } header.setContentProtection(isContentProtectionOn); header.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); - header.loadFile(path.join(__dirname, '../app/header.html')); + // header.loadFile(path.join(__dirname, '../app/header.html')); // Open DevTools in development if (!app.isPackaged) { @@ -1004,18 +494,10 @@ function createWindows() { header.on('resize', updateLayout); - header.webContents.once('dom-ready', () => { - loadAndRegisterShortcuts(); - }); - - ipcMain.handle('toggle-all-windows-visibility', toggleAllWindowsVisibility); + ipcMain.handle('toggle-all-windows-visibility', () => toggleAllWindowsVisibility(movementManager)); ipcMain.handle('toggle-feature', async (event, featureName) => { - if (!windowPool.get(featureName) && currentHeaderState === 'app') { - createFeatureWindows(windowPool.get('header')); - } - - if (!windowPool.get(featureName) && currentHeaderState === 'app') { + if (!windowPool.get(featureName) && currentHeaderState === 'main') { createFeatureWindows(windowPool.get('header')); } @@ -1023,10 +505,10 @@ function createWindows() { if (windowToToggle) { if (featureName === 'listen') { - const liveSummaryService = require('../features/listen/liveSummaryService'); - if (liveSummaryService.isSessionActive()) { + const listenService = global.listenService; + if (listenService && listenService.isSessionActive()) { console.log('[WindowManager] Listen session is active, closing it via toggle.'); - await liveSummaryService.closeSession(); + await listenService.closeSession(); return; } } @@ -1207,48 +689,36 @@ function createWindows() { } }); - setupIpcHandlers(); - return windowPool; } -function loadAndRegisterShortcuts() { +function loadAndRegisterShortcuts(movementManager) { + if (windowPool.has('shortcut-settings')) { + console.log('[Shortcuts] Editing in progress, skipping registration.'); + return; + } + const defaultKeybinds = getDefaultKeybinds(); - const header = windowPool.get('header'); + const savedKeybinds = shortCutStore.get('customKeybinds', {}); + const keybinds = { ...defaultKeybinds, ...savedKeybinds }; + const sendToRenderer = (channel, ...args) => { windowPool.forEach(win => { - try { - if (win && !win.isDestroyed()) { + if (win && !win.isDestroyed()) { + try { win.webContents.send(channel, ...args); + } catch (e) { + // 창이 이미 λ‹«ν˜”μ„ 수 μžˆμœΌλ―€λ‘œ 였λ₯˜λ₯Ό λ¬΄μ‹œν•©λ‹ˆλ‹€. } - } catch (e) {} + } }); }; - const openaiSessionRef = { current: null }; - - if (!header) { - return updateGlobalShortcuts(defaultKeybinds, undefined, sendToRenderer, openaiSessionRef); - } - - header.webContents - .executeJavaScript(`(() => localStorage.getItem('customKeybinds'))()`) - .then(saved => (saved ? JSON.parse(saved) : {})) - .then(savedKeybinds => { - const keybinds = { ...defaultKeybinds, ...savedKeybinds }; - updateGlobalShortcuts(keybinds, header, sendToRenderer, openaiSessionRef); - }) - .catch(() => updateGlobalShortcuts(defaultKeybinds, header, sendToRenderer, openaiSessionRef)); + updateGlobalShortcuts(keybinds, windowPool.get('header'), sendToRenderer, movementManager); } -function updateLayout() { - layoutManager.updateLayout(); -} - -function setupIpcHandlers(openaiSessionRef) { - const layoutManager = new WindowLayoutManager(); - // const movementManager = new SmoothMovementManager(); +function setupIpcHandlers(movementManager) { screen.on('display-added', (event, newDisplay) => { console.log('[Display] New display added:', newDisplay.id); }); @@ -1365,17 +835,6 @@ function setupIpcHandlers(openaiSessionRef) { app.quit(); }); - ipcMain.handle('message-sending', async event => { - console.log('πŸ“¨ Main: Received message-sending signal'); - const askWindow = windowPool.get('ask'); - if (askWindow && !askWindow.isDestroyed()) { - console.log('πŸ“€ Main: Sending hide-text-input to ask window'); - askWindow.webContents.send('hide-text-input'); - return { success: true }; - } - return { success: false }; - }); - ipcMain.handle('is-window-visible', (event, windowName) => { const window = windowPool.get(windowName); if (window && !window.isDestroyed()) { @@ -1404,11 +863,12 @@ function setupIpcHandlers(openaiSessionRef) { console.log(`[WindowManager] Header state changed to: ${state}`); currentHeaderState = state; - if (state === 'app') { + if (state === 'main') { createFeatureWindows(windowPool.get('header')); - } else { // 'apikey' + } else { // 'apikey' | 'permission' destroyFeatureWindows(); } + loadAndRegisterShortcuts(movementManager); for (const [name, win] of windowPool) { if (!isAllowed(name) && !win.isDestroyed()) { @@ -1418,76 +878,69 @@ function setupIpcHandlers(openaiSessionRef) { win.show(); } } - - const header = windowPool.get('header'); - if (header && !header.isDestroyed()) { - header.webContents - .executeJavaScript(`(() => localStorage.getItem('customKeybinds'))()`) - .then(saved => { - const defaultKeybinds = getDefaultKeybinds(); - const savedKeybinds = saved ? JSON.parse(saved) : {}; - const keybinds = { ...defaultKeybinds, ...savedKeybinds }; - - const sendToRenderer = (channel, ...args) => { - windowPool.forEach(win => { - try { - if (win && !win.isDestroyed()) { - win.webContents.send(channel, ...args); - } - } catch (e) {} - }); - }; - - updateGlobalShortcuts(keybinds, header, sendToRenderer, { current: null }); - }) - .catch(console.error); - } - }); - - ipcMain.handle('get-available-screens', async () => { - try { - const sources = await desktopCapturer.getSources({ - types: ['screen'], - thumbnailSize: { width: 300, height: 200 }, - }); - - const displays = screen.getAllDisplays(); - - return sources.map((source, index) => { - const display = displays[index] || displays[0]; - return { - id: source.id, - name: source.name, - thumbnail: source.thumbnail.toDataURL(), - display: { - id: display.id, - bounds: display.bounds, - workArea: display.workArea, - scaleFactor: display.scaleFactor, - isPrimary: display.id === screen.getPrimaryDisplay().id, - }, - }; - }); - } catch (error) { - console.error('Failed to get available screens:', error); - return []; - } - }); - - ipcMain.handle('set-capture-source', (event, sourceId) => { - selectedCaptureSourceId = sourceId; - console.log(`[Capture] Selected source: ${sourceId}`); - return { success: true }; - }); - - ipcMain.handle('get-capture-source', () => { - return selectedCaptureSourceId; }); ipcMain.on('update-keybinds', (event, newKeybinds) => { updateGlobalShortcuts(newKeybinds); }); + ipcMain.handle('get-current-shortcuts', () => { + const defaultKeybinds = getDefaultKeybinds(); + const savedKeybinds = shortCutStore.get('customKeybinds', {}); + return { ...defaultKeybinds, ...savedKeybinds }; + }); + + ipcMain.handle('open-shortcut-editor', () => { + const header = windowPool.get('header'); + if (!header) return; + + // νŽΈμ§‘κΈ° μ—΄κΈ° μ „ λͺ¨λ“  단좕킀 λΉ„ν™œμ„±ν™” + globalShortcut.unregisterAll(); + console.log('[Shortcuts] Disabled for editing.'); + + createFeatureWindows(header, 'shortcut-settings'); + }); + + ipcMain.handle('get-default-shortcuts', () => { + shortCutStore.set('customKeybinds', {}); + return getDefaultKeybinds(); + }); + + ipcMain.handle('save-shortcuts', async (event, newKeybinds) => { + try { + const defaultKeybinds = getDefaultKeybinds(); + const customKeybinds = {}; + for (const key in newKeybinds) { + if (newKeybinds[key] && newKeybinds[key] !== defaultKeybinds[key]) { + customKeybinds[key] = newKeybinds[key]; + } + } + + shortCutStore.set('customKeybinds', customKeybinds); + console.log('[Shortcuts] Custom keybinds saved to store:', customKeybinds); + + const editor = windowPool.get('shortcut-settings'); + if (editor && !editor.isDestroyed()) { + editor.close(); + } else { + loadAndRegisterShortcuts(movementManager); + } + + return { success: true }; + } catch (error) { + console.error("Failed to save shortcuts:", error); + loadAndRegisterShortcuts(movementManager); + return { success: false, error: error.message }; + } + }); + + ipcMain.on('close-shortcut-editor', () => { + const editor = windowPool.get('shortcut-settings'); + if (editor && !editor.isDestroyed()) { + editor.close(); + } + }); + ipcMain.handle('open-login-page', () => { const webUrl = process.env.pickleglass_WEB_URL || 'http://localhost:3000'; const personalizeUrl = `${webUrl}/personalize?desktop=true`; @@ -1581,12 +1034,6 @@ function setupIpcHandlers(openaiSessionRef) { } }); - ipcMain.on('move-to-edge', (event, direction) => { - if (movementManager) { - movementManager.moveToEdge(direction); - } - }); - ipcMain.handle('force-close-window', (event, windowName) => { const window = windowPool.get(windowName); if (window && !window.isDestroyed()) { @@ -1627,68 +1074,7 @@ function setupIpcHandlers(openaiSessionRef) { }); ipcMain.handle('capture-screenshot', async (event, options = {}) => { - if (process.platform === 'darwin') { - try { - const tempPath = path.join(os.tmpdir(), `screenshot-${Date.now()}.jpg`); - - await execFile('screencapture', ['-x', '-t', 'jpg', tempPath]); - - const imageBuffer = await fs.promises.readFile(tempPath); - await fs.promises.unlink(tempPath); - - const resizedBuffer = await sharp(imageBuffer) - // .resize({ height: 1080 }) - .resize({ height: 384 }) - .jpeg({ quality: 80 }) - .toBuffer(); - - const base64 = resizedBuffer.toString('base64'); - const metadata = await sharp(resizedBuffer).metadata(); - - lastScreenshot = { - base64, - width: metadata.width, - height: metadata.height, - timestamp: Date.now(), - }; - - return { success: true, base64, width: metadata.width, height: metadata.height }; - } catch (error) { - console.error('Failed to capture and resize screenshot:', error); - return { success: false, error: error.message }; - } - } - - try { - const sources = await desktopCapturer.getSources({ - types: ['screen'], - thumbnailSize: { - width: 1920, - height: 1080, - }, - }); - - if (sources.length === 0) { - throw new Error('No screen sources available'); - } - const source = sources[0]; - const buffer = source.thumbnail.toJPEG(70); - const base64 = buffer.toString('base64'); - const size = source.thumbnail.getSize(); - - return { - success: true, - base64, - width: size.width, - height: size.height, - }; - } catch (error) { - console.error('Failed to capture screenshot using desktopCapturer:', error); - return { - success: false, - error: error.message, - }; - } + return captureScreenshot(options); }); ipcMain.handle('get-current-screenshot', async event => { @@ -1715,131 +1101,10 @@ function setupIpcHandlers(openaiSessionRef) { } }); - ipcMain.handle('firebase-auth-state-changed', (event, user) => { - console.log('[WindowManager] Firebase auth state changed:', user ? user.email : 'null'); - const previousUser = currentFirebaseUser; - - // πŸ›‘οΈ Guard: ignore duplicate events where auth state did not actually change - const sameUser = user && previousUser && user.uid && previousUser.uid && user.uid === previousUser.uid; - const bothNull = !user && !previousUser; - if (sameUser || bothNull) { - // No real state change ➜ skip further processing - console.log('[WindowManager] No real state change, skipping further processing'); - return; - } - - currentFirebaseUser = user; - - if (user && user.email) { - (async () => { - try { - const existingKey = getStoredApiKey(); - if (existingKey) { - console.log('[WindowManager] Virtual key already exists, skipping fetch'); - return; - } - - if (!user.idToken) { - console.warn('[WindowManager] No ID token available, cannot fetch virtual key'); - return; - } - - console.log('[WindowManager] Fetching virtual key via onAuthStateChanged'); - const vKey = await getVirtualKeyByEmail(user.email, user.idToken); - console.log('[WindowManager] Virtual key fetched successfully'); - - setApiKey(vKey) - .then(() => { - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.webContents.send('api-key-updated'); - } - }); - }) - .catch(err => console.error('[WindowManager] Failed to save virtual key:', err)); - } catch (err) { - console.error('[WindowManager] Virtual key fetch failed:', err); - - if (err.message.includes('token') || err.message.includes('Authentication')) { - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.webContents.send('auth-error', { - message: 'Authentication expired. Please login again.', - shouldLogout: true, - }); - } - }); - } - } - })(); - } - - // If the user logged out, also hide the settings window - if (!user && previousUser) { - // ADDED: Only trigger on actual state change from logged in to logged out - console.log('[WindowManager] User logged out, clearing API key and notifying renderers'); - - setApiKey(null) - .then(() => { - console.log('[WindowManager] API key cleared successfully after logout'); - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.webContents.send('api-key-removed'); - } - }); - }) - .catch(err => { - console.error('[WindowManager] setApiKey error:', err); - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.webContents.send('api-key-removed'); - } - }); - }); - - const settingsWindow = windowPool.get('settings'); - if (settingsWindow && settingsWindow.isVisible()) { - settingsWindow.hide(); - console.log('[WindowManager] Settings window hidden after logout.'); - } - } - // Broadcast to all windows - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.webContents.send('firebase-user-updated', user); - } - }); - }); - - ipcMain.handle('get-current-firebase-user', () => { - return currentFirebaseUser; - }); - - ipcMain.handle('firebase-logout', () => { + ipcMain.handle('firebase-logout', async () => { console.log('[WindowManager] Received request to log out.'); - setApiKey(null) - .then(() => { - console.log('[WindowManager] API key cleared successfully after logout'); - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.webContents.send('api-key-removed'); - } - }); - }) - .catch(err => { - console.error('[WindowManager] setApiKey error:', err); - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - win.webContents.send('api-key-removed'); - } - }); - }); - - const header = windowPool.get('header'); - if (header && !header.isDestroyed()) { - console.log('[WindowManager] Header window exists, sending to renderer...'); - header.webContents.send('request-firebase-logout'); - } + + await authService.signOut(); }); ipcMain.handle('check-system-permissions', async () => { @@ -1898,7 +1163,7 @@ function setupIpcHandlers(openaiSessionRef) { // Req mic permission const granted = await systemPreferences.askForMediaAccess('microphone'); return { - success: granted, + success: granted, status: granted ? 'granted' : 'denied' }; } catch (error) { @@ -1944,11 +1209,8 @@ function setupIpcHandlers(openaiSessionRef) { ipcMain.handle('mark-permissions-completed', async () => { try { - // Store in SQLite that permissions have been completed - await sqliteClient.query( - 'INSERT OR REPLACE INTO system_settings (key, value) VALUES (?, ?)', - ['permissions_completed', 'true'] - ); + // This is a system-level setting, not user-specific. + await systemSettingsRepository.markPermissionsAsCompleted(); console.log('[Permissions] Marked permissions as completed'); return { success: true }; } catch (error) { @@ -1959,11 +1221,7 @@ function setupIpcHandlers(openaiSessionRef) { ipcMain.handle('check-permissions-completed', async () => { try { - const result = await sqliteClient.query( - 'SELECT value FROM system_settings WHERE key = ?', - ['permissions_completed'] - ); - const completed = result.length > 0 && result[0].value === 'true'; + const completed = await systemSettingsRepository.checkPermissionsCompleted(); console.log('[Permissions] Permissions completed status:', completed); return completed; } catch (error) { @@ -1971,234 +1229,66 @@ function setupIpcHandlers(openaiSessionRef) { return false; } }); -} - - -let storedApiKey = null; -let storedProvider = 'openai'; - -async function setApiKey(apiKey, provider = 'openai') { - storedApiKey = apiKey; - storedProvider = provider; - console.log('[WindowManager] API key and provider stored (and will be persisted to DB)'); - - try { - await sqliteClient.saveApiKey(apiKey, sqliteClient.defaultUserId, provider); - console.log('[WindowManager] API key and provider saved to SQLite'); - } catch (err) { - console.error('[WindowManager] Failed to save API key to SQLite:', err); - } - - windowPool.forEach(win => { - if (win && !win.isDestroyed()) { - const js = apiKey ? ` - localStorage.setItem('openai_api_key', ${JSON.stringify(apiKey)}); - localStorage.setItem('ai_provider', ${JSON.stringify(provider)}); - ` : ` - localStorage.removeItem('openai_api_key'); - localStorage.removeItem('ai_provider'); - `; - win.webContents.executeJavaScript(js).catch(() => {}); + ipcMain.handle('close-ask-window-if-empty', async () => { + const askWindow = windowPool.get('ask'); + if (askWindow && !askWindow.isFocused()) { + askWindow.hide(); } }); } -async function loadApiKeyFromDb() { - try { - const user = await sqliteClient.getUser(sqliteClient.defaultUserId); - if (user && user.api_key) { - console.log('[WindowManager] API key and provider loaded from SQLite for default user.'); - storedApiKey = user.api_key; - storedProvider = user.provider || 'openai'; - return user.api_key; - } - return null; - } catch (error) { - console.error('[WindowManager] Failed to load API key from SQLite:', error); - return null; + + +//////// after_modelStateService //////// +async function getStoredApiKey() { + if (global.modelStateService) { + const provider = await getStoredProvider(); + return global.modelStateService.getApiKey(provider); } + return null; // Fallback } -function getCurrentFirebaseUser() { - return currentFirebaseUser; +async function getStoredProvider() { + if (global.modelStateService) { + return global.modelStateService.getCurrentProvider('llm'); + } + return 'openai'; // Fallback } -function isFirebaseLoggedIn() { - return !!currentFirebaseUser; -} - -function setCurrentFirebaseUser(user) { - currentFirebaseUser = user; - console.log('[WindowManager] Firebase user updated:', user ? user.email : 'null'); -} - -function getStoredApiKey() { - return storedApiKey; -} - -function getStoredProvider() { - return storedProvider || 'openai'; +/** + * + * @param {IpcMainInvokeEvent} event + * @param {{type: 'llm' | 'stt'}} + */ +async function getCurrentModelInfo(event, { type }) { + if (global.modelStateService && (type === 'llm' || type === 'stt')) { + return global.modelStateService.getCurrentModelInfo(type); + } + return null; } function setupApiKeyIPC() { const { ipcMain } = require('electron'); - ipcMain.handle('get-stored-api-key', async () => { - if (storedApiKey === null) { - const dbKey = await loadApiKeyFromDb(); - if (dbKey) { - await setApiKey(dbKey, storedProvider); - } - } - return storedApiKey; - }); + ipcMain.handle('get-stored-api-key', getStoredApiKey); + ipcMain.handle('get-ai-provider', getStoredProvider); + ipcMain.handle('get-current-model-info', getCurrentModelInfo); ipcMain.handle('api-key-validated', async (event, data) => { - console.log('[WindowManager] API key validation completed, saving...'); - - // Support both old format (string) and new format (object) - const apiKey = typeof data === 'string' ? data : data.apiKey; - const provider = typeof data === 'string' ? 'openai' : (data.provider || 'openai'); - - await setApiKey(apiKey, provider); - - windowPool.forEach((win, name) => { - if (win && !win.isDestroyed()) { - win.webContents.send('api-key-validated', { apiKey, provider }); - } - }); - + console.warn("[DEPRECATED] 'api-key-validated' IPC was called. This logic is now handled by 'model:validate-key'."); return { success: true }; }); ipcMain.handle('remove-api-key', async () => { - console.log('[WindowManager] API key removal requested'); - await setApiKey(null); - - windowPool.forEach((win, name) => { - if (win && !win.isDestroyed()) { - win.webContents.send('api-key-removed'); - } - }); - - const settingsWindow = windowPool.get('settings'); - if (settingsWindow && settingsWindow.isVisible()) { - settingsWindow.hide(); - console.log('[WindowManager] Settings window hidden after clearing API key.'); - } - + console.warn("[DEPRECATED] 'remove-api-key' IPC was called. This is now handled by 'model:remove-api-key'."); return { success: true }; }); - - ipcMain.handle('get-current-api-key', async () => { - if (storedApiKey === null) { - const dbKey = await loadApiKeyFromDb(); - if (dbKey) { - await setApiKey(dbKey, storedProvider); - } - } - return storedApiKey; - }); - ipcMain.handle('get-ai-provider', async () => { - console.log('[WindowManager] AI provider requested from renderer'); - return storedProvider || 'openai'; - }); - - console.log('[WindowManager] API key related IPC handlers registered (SQLite-backed)'); + console.log('[WindowManager] API key related IPC handlers have been updated for ModelStateService.'); } +//////// after_modelStateService //////// -function createWindow(sendToRenderer, openaiSessionRef) { - const mainWindow = new BrowserWindow({ - width: DEFAULT_WINDOW_WIDTH, - height: HEADER_HEIGHT, - x: initialX, - y: initialY, - frame: false, - transparent: false, - hasShadow: true, - alwaysOnTop: true, - skipTaskbar: true, - hiddenInMissionControl: true, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - backgroundThrottling: false, - enableBlinkFeatures: 'GetDisplayMedia', - webSecurity: true, - allowRunningInsecureContent: false, - }, - backgroundColor: '#FF0000', - }); - - const { session, desktopCapturer } = require('electron'); - session.defaultSession.setDisplayMediaRequestHandler( - (request, callback) => { - desktopCapturer.getSources({ types: ['screen'] }).then(sources => { - callback({ video: sources[0], audio: 'loopback' }); - }); - }, - { useSystemPicker: true } - ); - - mainWindow.setResizable(false); - mainWindow.setContentProtection(true); - mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); - - const primaryDisplay = screen.getPrimaryDisplay(); - const { width: screenWidth } = primaryDisplay.workAreaSize; - const x = Math.floor((screenWidth - DEFAULT_WINDOW_WIDTH) / 2); - const y = 0; - mainWindow.setPosition(x, y); - - if (process.platform === 'win32') { - mainWindow.setAlwaysOnTop(true, 'screen-saver', 1); - } - - mainWindow.loadFile(path.join(__dirname, '../index.html')); - - mainWindow.webContents.once('dom-ready', () => { - setTimeout(() => { - const defaultKeybinds = getDefaultKeybinds(); - let keybinds = defaultKeybinds; - - mainWindow.webContents - .executeJavaScript( - ` - (() => { - try { - const savedKeybinds = localStorage.getItem('customKeybinds'); - const savedContentProtection = localStorage.getItem('contentProtection'); - - return { - keybinds: savedKeybinds ? JSON.parse(savedKeybinds) : null, - contentProtection: savedContentProtection !== null ? savedContentProtection === 'true' : true - }; - } catch (e) { - return { keybinds: null, contentProtection: true }; - } - })() - ` - ) - .then(savedSettings => { - if (savedSettings.keybinds) { - keybinds = { ...defaultKeybinds, ...savedSettings.keybinds }; - } - mainWindow.setContentProtection(savedSettings.contentProtection); - updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, openaiSessionRef); - }) - .catch(() => { - mainWindow.setContentProtection(true); - updateGlobalShortcuts(defaultKeybinds, mainWindow, sendToRenderer, openaiSessionRef); - }); - }, 150); - }); - - setupWindowIpcHandlers(mainWindow, sendToRenderer, openaiSessionRef); - - return mainWindow; -} function getDefaultKeybinds() { const isMac = process.platform === 'darwin'; @@ -2218,39 +1308,27 @@ function getDefaultKeybinds() { }; } -function updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, openaiSessionRef) { - console.log('Updating global shortcuts with:', keybinds); - - // Unregister all existing shortcuts +function updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, movementManager) { globalShortcut.unregisterAll(); - if (movementManager) { - movementManager.destroy(); + if (sendToRenderer) { + sendToRenderer('shortcuts-updated', keybinds); + console.log('[Shortcuts] Broadcasted updated shortcuts to all windows.'); } - movementManager = new SmoothMovementManager(); - + + // ✨ ν•˜λ“œμ½”λ”©λœ 단좕킀 등둝을 μœ„ν•΄ λ³€μˆ˜ μœ μ§€ const isMac = process.platform === 'darwin'; const modifier = isMac ? 'Cmd' : 'Ctrl'; + const header = windowPool.get('header'); + const state = header?.currentHeaderState || currentHeaderState; - if (keybinds.toggleVisibility) { - try { - globalShortcut.register(keybinds.toggleVisibility, toggleAllWindowsVisibility); - console.log(`Registered toggleVisibility: ${keybinds.toggleVisibility}`); - } catch (error) { - console.error(`Failed to register toggleVisibility (${keybinds.toggleVisibility}):`, error); - } - } - + // ✨ κΈ°λŠ₯ 1: μ‚¬μš©μžκ°€ μ„€μ •ν•  수 μ—†λŠ” 'λͺ¨λ‹ˆν„° 이동' 단좕킀 (κΈ°μ‘΄ 둜직 μœ μ§€) const displays = screen.getAllDisplays(); if (displays.length > 1) { displays.forEach((display, index) => { const key = `${modifier}+Shift+${index + 1}`; try { - globalShortcut.register(key, () => { - if (movementManager) { - movementManager.moveToDisplay(display.id); - } - }); + globalShortcut.register(key, () => movementManager.moveToDisplay(display.id)); console.log(`Registered display switch shortcut: ${key} -> Display ${index + 1}`); } catch (error) { console.error(`Failed to register display switch ${key}:`, error); @@ -2258,254 +1336,161 @@ function updateGlobalShortcuts(keybinds, mainWindow, sendToRenderer, openaiSessi }); } - if (currentHeaderState === 'apikey') { + // API ν‚€ μž…λ ₯ μƒνƒœμ—μ„œλŠ” ν•„μˆ˜ 단좕킀(toggleVisibility) μ™Έμ—λŠ” 아무것도 λ“±λ‘ν•˜μ§€ μ•ŠμŒ + if (state === 'apikey') { + if (keybinds.toggleVisibility) { + try { + globalShortcut.register(keybinds.toggleVisibility, () => toggleAllWindowsVisibility(movementManager)); + } catch (error) { + console.error(`Failed to register toggleVisibility (${keybinds.toggleVisibility}):`, error); + } + } console.log('ApiKeyHeader is active, skipping conditional shortcuts'); return; } - const directions = [ - { key: `${modifier}+Left`, direction: 'left' }, - { key: `${modifier}+Right`, direction: 'right' }, - { key: `${modifier}+Up`, direction: 'up' }, - { key: `${modifier}+Down`, direction: 'down' }, - ]; - - directions.forEach(({ key, direction }) => { - try { - globalShortcut.register(key, () => { - const header = windowPool.get('header'); - if (header && header.isVisible()) { - movementManager.moveStep(direction); - } - }); - console.log(`Registered global shortcut: ${key} -> ${direction}`); - } catch (error) { - console.error(`Failed to register ${key}:`, error); - } - }); - + // ✨ κΈ°λŠ₯ 2: μ‚¬μš©μžκ°€ μ„€μ •ν•  수 μ—†λŠ” 'ν™”λ©΄ κ°€μž₯자리 이동' 단좕킀 (κΈ°μ‘΄ 둜직 μœ μ§€) const edgeDirections = [ { key: `${modifier}+Shift+Left`, direction: 'left' }, { key: `${modifier}+Shift+Right`, direction: 'right' }, - { key: `${modifier}+Shift+Up`, direction: 'up' }, - { key: `${modifier}+Shift+Down`, direction: 'down' }, + // { key: `${modifier}+Shift+Up`, direction: 'up' }, + // { key: `${modifier}+Shift+Down`, direction: 'down' }, ]; - edgeDirections.forEach(({ key, direction }) => { try { globalShortcut.register(key, () => { - const header = windowPool.get('header'); - if (header && header.isVisible()) { - movementManager.moveToEdge(direction); - } + if (header && header.isVisible()) movementManager.moveToEdge(direction); }); - console.log(`Registered global shortcut: ${key} -> edge ${direction}`); } catch (error) { - console.error(`Failed to register ${key}:`, error); + console.error(`Failed to register edge move for ${key}:`, error); } }); - if (keybinds.toggleClickThrough) { + + // ✨ κΈ°λŠ₯ 3: μ‚¬μš©μžκ°€ μ„€μ • κ°€λŠ₯ν•œ λͺ¨λ“  단좕킀λ₯Ό λ™μ μœΌλ‘œ 등둝 (μƒˆλ‘œμš΄ 방식 적용) + for (const action in keybinds) { + const accelerator = keybinds[action]; + if (!accelerator) continue; + try { - globalShortcut.register(keybinds.toggleClickThrough, () => { - mouseEventsIgnored = !mouseEventsIgnored; - if (mouseEventsIgnored) { - mainWindow.setIgnoreMouseEvents(true, { forward: true }); - console.log('Mouse events ignored'); - } else { - mainWindow.setIgnoreMouseEvents(false); - console.log('Mouse events enabled'); - } - mainWindow.webContents.send('click-through-toggled', mouseEventsIgnored); - }); - console.log(`Registered toggleClickThrough: ${keybinds.toggleClickThrough}`); - } catch (error) { - console.error(`Failed to register toggleClickThrough (${keybinds.toggleClickThrough}):`, error); - } - } - - if (keybinds.nextStep) { - try { - globalShortcut.register(keybinds.nextStep, () => { - console.log('⌘/Ctrl+Enter Ask shortcut triggered'); - - const askWindow = windowPool.get('ask'); - if (!askWindow || askWindow.isDestroyed()) { - console.error('Ask window not found or destroyed'); - return; - } - - if (askWindow.isVisible()) { - askWindow.webContents.send('ask-global-send'); - } else { - try { - askWindow.show(); - - const header = windowPool.get('header'); - if (header) { - const currentHeaderPosition = header.getBounds(); + let callback; + switch(action) { + case 'toggleVisibility': + callback = () => toggleAllWindowsVisibility(movementManager); + break; + case 'nextStep': + callback = () => { + const askWindow = windowPool.get('ask'); + if (!askWindow || askWindow.isDestroyed()) return; + if (askWindow.isVisible()) { + askWindow.webContents.send('ask-global-send'); + } else { + askWindow.show(); updateLayout(); - header.setPosition(currentHeaderPosition.x, currentHeaderPosition.y, false); + askWindow.webContents.send('window-show-animation'); } - - askWindow.webContents.send('window-show-animation'); - } catch (e) { - console.error('Error showing Ask window:', e); - } - } - }); - console.log(`Registered Ask shortcut (nextStep): ${keybinds.nextStep}`); - } catch (error) { - console.error(`Failed to register Ask shortcut (${keybinds.nextStep}):`, error); - } - } - - if (keybinds.manualScreenshot) { - try { - globalShortcut.register(keybinds.manualScreenshot, () => { - console.log('Manual screenshot shortcut triggered'); - mainWindow.webContents.executeJavaScript(` - if (window.captureManualScreenshot) { - window.captureManualScreenshot(); - } else { - console.log('Manual screenshot function not available'); - } - `); - }); - console.log(`Registered manualScreenshot: ${keybinds.manualScreenshot}`); - } catch (error) { - console.error(`Failed to register manualScreenshot (${keybinds.manualScreenshot}):`, error); - } - } - - if (keybinds.previousResponse) { - try { - globalShortcut.register(keybinds.previousResponse, () => { - console.log('Previous response shortcut triggered'); - sendToRenderer('navigate-previous-response'); - }); - console.log(`Registered previousResponse: ${keybinds.previousResponse}`); - } catch (error) { - console.error(`Failed to register previousResponse (${keybinds.previousResponse}):`, error); - } - } - - if (keybinds.nextResponse) { - try { - globalShortcut.register(keybinds.nextResponse, () => { - console.log('Next response shortcut triggered'); - sendToRenderer('navigate-next-response'); - }); - console.log(`Registered nextResponse: ${keybinds.nextResponse}`); - } catch (error) { - console.error(`Failed to register nextResponse (${keybinds.nextResponse}):`, error); - } - } - - if (keybinds.scrollUp) { - try { - globalShortcut.register(keybinds.scrollUp, () => { - console.log('Scroll up shortcut triggered'); - sendToRenderer('scroll-response-up'); - }); - console.log(`Registered scrollUp: ${keybinds.scrollUp}`); - } catch (error) { - console.error(`Failed to register scrollUp (${keybinds.scrollUp}):`, error); - } - } - - if (keybinds.scrollDown) { - try { - globalShortcut.register(keybinds.scrollDown, () => { - console.log('Scroll down shortcut triggered'); - sendToRenderer('scroll-response-down'); - }); - console.log(`Registered scrollDown: ${keybinds.scrollDown}`); - } catch (error) { - console.error(`Failed to register scrollDown (${keybinds.scrollDown}):`, error); - } - } -} - -function setupWindowIpcHandlers(mainWindow, sendToRenderer, openaiSessionRef) { - ipcMain.handle('resize-window', async (event, args) => { - try { - const { isMainViewVisible, view } = args; - let targetHeight = HEADER_HEIGHT; - let targetWidth = DEFAULT_WINDOW_WIDTH; - - if (isMainViewVisible) { - const viewHeights = { - listen: 400, - customize: 600, - help: 550, - history: 550, - setup: 200, - }; - targetHeight = viewHeights[view] || 400; + }; + break; + case 'scrollUp': + callback = () => { + // 'ask' 창을 λͺ…μ‹œμ μœΌλ‘œ κ°€μ Έμ˜΅λ‹ˆλ‹€. + const askWindow = windowPool.get('ask'); + // 'ask' 창이 μ‘΄μž¬ν•˜κ³ , νŒŒκ΄΄λ˜μ§€ μ•Šμ•˜μœΌλ©°, λ³΄μ΄λŠ” κ²½μš°μ—λ§Œ 이벀트λ₯Ό μ „μ†‘ν•©λ‹ˆλ‹€. + if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) { + askWindow.webContents.send('scroll-response-up'); + } + }; + break; + case 'scrollDown': + callback = () => { + // 'ask' 창을 λͺ…μ‹œμ μœΌλ‘œ κ°€μ Έμ˜΅λ‹ˆλ‹€. + const askWindow = windowPool.get('ask'); + // 'ask' 창이 μ‘΄μž¬ν•˜κ³ , νŒŒκ΄΄λ˜μ§€ μ•Šμ•˜μœΌλ©°, λ³΄μ΄λŠ” κ²½μš°μ—λ§Œ 이벀트λ₯Ό μ „μ†‘ν•©λ‹ˆλ‹€. + if (askWindow && !askWindow.isDestroyed() && askWindow.isVisible()) { + askWindow.webContents.send('scroll-response-down'); + } + }; + break; + case 'moveUp': + callback = () => { if (header && header.isVisible()) movementManager.moveStep('up'); }; + break; + case 'moveDown': + callback = () => { if (header && header.isVisible()) movementManager.moveStep('down'); }; + break; + case 'moveLeft': + callback = () => { if (header && header.isVisible()) movementManager.moveStep('left'); }; + break; + case 'moveRight': + callback = () => { if (header && header.isVisible()) movementManager.moveStep('right'); }; + break; + case 'toggleClickThrough': + callback = () => { + mouseEventsIgnored = !mouseEventsIgnored; + if(mainWindow && !mainWindow.isDestroyed()){ + mainWindow.setIgnoreMouseEvents(mouseEventsIgnored, { forward: true }); + mainWindow.webContents.send('click-through-toggled', mouseEventsIgnored); + } + }; + break; + case 'manualScreenshot': + callback = () => { + if(mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.executeJavaScript('window.captureManualScreenshot && window.captureManualScreenshot();'); + } + }; + break; + case 'previousResponse': + callback = () => sendToRenderer('navigate-previous-response'); + break; + case 'nextResponse': + callback = () => sendToRenderer('navigate-next-response'); + break; } - - const [currentWidth, currentHeight] = mainWindow.getSize(); - if (currentWidth !== targetWidth || currentHeight !== targetHeight) { - console.log('Window resize requested but disabled for manual resize prevention'); + + if (callback) { + globalShortcut.register(accelerator, callback); } + } catch(e) { + console.error(`Failed to register shortcut for "${action}" (${accelerator}):`, e.message); + } + } +} + + +async function captureScreenshot(options = {}) { + if (process.platform === 'darwin') { + try { + const tempPath = path.join(os.tmpdir(), `screenshot-${Date.now()}.jpg`); + + await execFile('screencapture', ['-x', '-t', 'jpg', tempPath]); + + const imageBuffer = await fs.promises.readFile(tempPath); + await fs.promises.unlink(tempPath); + + const resizedBuffer = await sharp(imageBuffer) + // .resize({ height: 1080 }) + .resize({ height: 384 }) + .jpeg({ quality: 80 }) + .toBuffer(); + + const base64 = resizedBuffer.toString('base64'); + const metadata = await sharp(resizedBuffer).metadata(); + + lastScreenshot = { + base64, + width: metadata.width, + height: metadata.height, + timestamp: Date.now(), + }; + + return { success: true, base64, width: metadata.width, height: metadata.height }; } catch (error) { - console.error('Error resizing window:', error); + console.error('Failed to capture and resize screenshot:', error); + return { success: false, error: error.message }; } - }); - - ipcMain.handle('toggle-window-visibility', async event => { - if (mainWindow.isVisible()) { - mainWindow.hide(); - } else { - mainWindow.show(); - } - }); - - ipcMain.handle('quit-application', async () => { - app.quit(); - }); - - // Keep other essential IPC handlers - // ... other handlers like open-external, etc. can be added from the old file if needed -} - -function clearApiKey() { - setApiKey(null); -} - -async function getVirtualKeyByEmail(email, idToken) { - if (!idToken) { - throw new Error('Firebase ID token is required for virtual key request'); } - const resp = await fetch('https://serverless-api-sf3o.vercel.app/api/virtual_key', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${idToken}`, - }, - body: JSON.stringify({ email: email.trim().toLowerCase() }), - redirect: 'follow', - }); - - const json = await resp.json().catch(() => ({})); - if (!resp.ok) { - console.error('[VK] API request failed:', json.message || 'Unknown error'); - throw new Error(json.message || `HTTP ${resp.status}: Virtual key request failed`); - } - - const vKey = json?.data?.virtualKey || json?.data?.virtual_key || json?.data?.newVKey?.slug; - - if (!vKey) throw new Error('virtual key missing in response'); - return vKey; -} - -// Helper function to avoid code duplication -async function captureScreenshotInternal(options = {}) { try { - const quality = options.quality || 'medium'; - const sources = await desktopCapturer.getSources({ types: ['screen'], thumbnailSize: { @@ -2517,28 +1502,10 @@ async function captureScreenshotInternal(options = {}) { if (sources.length === 0) { throw new Error('No screen sources available'); } - const source = sources[0]; - const thumbnail = source.thumbnail; - - let jpegQuality; - switch (quality) { - case 'high': - jpegQuality = 90; - break; - case 'low': - jpegQuality = 50; - break; - case 'medium': - default: - jpegQuality = 70; - break; - } - - const buffer = thumbnail.toJPEG(jpegQuality); + const buffer = source.thumbnail.toJPEG(70); const base64 = buffer.toString('base64'); - - const size = thumbnail.getSize(); + const size = source.thumbnail.getSize(); return { success: true, @@ -2547,7 +1514,11 @@ async function captureScreenshotInternal(options = {}) { height: size.height, }; } catch (error) { - throw error; + console.error('Failed to capture screenshot using desktopCapturer:', error); + return { + success: false, + error: error.message, + }; } } @@ -2555,12 +1526,11 @@ module.exports = { createWindows, windowPool, fixedYPosition, - setApiKey, + //////// before_modelStateService //////// + // setApiKey, + //////// before_modelStateService //////// getStoredApiKey, getStoredProvider, - clearApiKey, - getCurrentFirebaseUser, - isFirebaseLoggedIn, - setCurrentFirebaseUser, - getVirtualKeyByEmail, -}; + getCurrentModelInfo, + captureScreenshot, +}; \ No newline at end of file diff --git a/src/features/ask/AskView.js b/src/features/ask/AskView.js index de58a86..ed4794f 100644 --- a/src/features/ask/AskView.js +++ b/src/features/ask/AskView.js @@ -596,6 +596,42 @@ export class AskView extends LitElement { color: rgba(255, 255, 255, 0.5); font-size: 14px; } + + /* ────────────────[ GLASS BYPASS ]─────────────── */ + :host-context(body.has-glass) .ask-container, + :host-context(body.has-glass) .response-header, + :host-context(body.has-glass) .response-icon, + :host-context(body.has-glass) .copy-button, + :host-context(body.has-glass) .close-button, + :host-context(body.has-glass) .line-copy-button, + :host-context(body.has-glass) .text-input-container, + :host-context(body.has-glass) .response-container pre, + :host-context(body.has-glass) .response-container p code, + :host-context(body.has-glass) .response-container pre code { + background: transparent !important; + border: none !important; + outline: none !important; + box-shadow: none !important; + filter: none !important; + backdrop-filter: none !important; + } + + :host-context(body.has-glass) .ask-container::before { + display: none !important; + } + + :host-context(body.has-glass) .copy-button:hover, + :host-context(body.has-glass) .close-button:hover, + :host-context(body.has-glass) .line-copy-button, + :host-context(body.has-glass) .line-copy-button:hover, + :host-context(body.has-glass) .response-line:hover { + background: transparent !important; + } + + :host-context(body.has-glass) .response-container::-webkit-scrollbar-track, + :host-context(body.has-glass) .response-container::-webkit-scrollbar-thumb { + background: transparent !important; + } `; constructor() { @@ -628,6 +664,8 @@ export class AskView extends LitElement { this.handleDocumentClick = this.handleDocumentClick.bind(this); this.handleWindowBlur = this.handleWindowBlur.bind(this); + this.handleScroll = this.handleScroll.bind(this); + this.loadLibraries(); // --- Resize helpers --- @@ -709,9 +747,10 @@ export class AskView extends LitElement { handleWindowBlur() { if (!this.currentResponse && !this.isLoading && !this.isStreaming) { - const askWindow = window.require('electron').remote.getCurrentWindow(); - if (!askWindow.isFocused()) { - this.closeIfNoContent(); + // If there's no active content, ask the main process to close this window. + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('close-ask-window-if-empty'); } } } @@ -799,13 +838,10 @@ export class AskView extends LitElement { this.processAssistantQuestion(question); }; - - if (window.require) { const { ipcRenderer } = window.require('electron'); ipcRenderer.on('ask-global-send', this.handleGlobalSendRequest); ipcRenderer.on('toggle-text-input', this.handleToggleTextInput); - ipcRenderer.on('clear-ask-content', this.clearResponseContent); ipcRenderer.on('receive-question-from-assistant', this.handleQuestionFromAssistant); ipcRenderer.on('hide-text-input', () => { console.log('πŸ“€ Hide text input signal received'); @@ -829,17 +865,15 @@ export class AskView extends LitElement { ipcRenderer.on('window-blur', this.handleWindowBlur); ipcRenderer.on('window-did-show', () => { if (!this.currentResponse && !this.isLoading && !this.isStreaming) { - setTimeout(() => { - const textInput = this.shadowRoot?.getElementById('textInput'); - if (textInput) { - textInput.focus(); - } - }, 100); + this.focusTextInput(); } }); ipcRenderer.on('ask-response-chunk', this.handleStreamChunk); ipcRenderer.on('ask-response-stream-end', this.handleStreamEnd); + + ipcRenderer.on('scroll-response-up', () => this.handleScroll('up')); + ipcRenderer.on('scroll-response-down', () => this.handleScroll('down')); console.log('βœ… AskView: IPC 이벀트 λ¦¬μŠ€λ„ˆ 등둝 μ™„λ£Œ'); } } @@ -871,7 +905,6 @@ export class AskView extends LitElement { const { ipcRenderer } = window.require('electron'); ipcRenderer.removeListener('ask-global-send', this.handleGlobalSendRequest); ipcRenderer.removeListener('toggle-text-input', this.handleToggleTextInput); - ipcRenderer.removeListener('clear-ask-content', this.clearResponseContent); ipcRenderer.removeListener('clear-ask-response', () => {}); ipcRenderer.removeListener('hide-text-input', () => {}); ipcRenderer.removeListener('window-hide-animation', () => {}); @@ -879,9 +912,24 @@ export class AskView extends LitElement { ipcRenderer.removeListener('ask-response-chunk', this.handleStreamChunk); ipcRenderer.removeListener('ask-response-stream-end', this.handleStreamEnd); + + ipcRenderer.removeListener('scroll-response-up', () => this.handleScroll('up')); + ipcRenderer.removeListener('scroll-response-down', () => this.handleScroll('down')); console.log('βœ… AskView: IPC 이벀트 λ¦¬μŠ€λ„ˆ 제거 μ™„λ£Œ'); } } + + handleScroll(direction) { + const scrollableElement = this.shadowRoot.querySelector('#responseContainer'); + if (scrollableElement) { + const scrollAmount = 100; // ν•œ λ²ˆμ— μŠ€ν¬λ‘€ν•  μ–‘ (px) + if (direction === 'up') { + scrollableElement.scrollTop -= scrollAmount; + } else { + scrollableElement.scrollTop += scrollAmount; + } + } + } // --- 슀트리밍 처리 ν•Έλ“€λŸ¬ --- handleStreamChunk(event, { token }) { @@ -1060,8 +1108,6 @@ export class AskView extends LitElement { }, 1500); } - - renderMarkdown(content) { if (!content) return ''; @@ -1131,13 +1177,16 @@ export class AskView extends LitElement { this.requestUpdate(); this.renderContent(); - window.pickleGlass.sendMessage(question).catch(error => { - console.error('Error processing assistant question:', error); - this.isLoading = false; - this.isStreaming = false; - this.currentResponse = `Error: ${error.message}`; - this.renderContent(); - }); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('ask:sendMessage', question).catch(error => { + console.error('Error processing assistant question:', error); + this.isLoading = false; + this.isStreaming = false; + this.currentResponse = `Error: ${error.message}`; + this.renderContent(); + }); + } } async handleCopy() { @@ -1227,16 +1276,24 @@ export class AskView extends LitElement { this.requestUpdate(); this.renderContent(); - window.pickleGlass.sendMessage(text).catch(error => { - console.error('Error sending text:', error); - this.isLoading = false; - this.isStreaming = false; - this.currentResponse = `Error: ${error.message}`; - this.renderContent(); - }); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('ask:sendMessage', text).catch(error => { + console.error('Error sending text:', error); + this.isLoading = false; + this.isStreaming = false; + this.currentResponse = `Error: ${error.message}`; + this.renderContent(); + }); + } } handleTextKeydown(e) { + // Fix for IME composition issue: Ignore Enter key presses while composing. + if (e.isComposing) { + return; + } + const isPlainEnter = e.key === 'Enter' && !e.shiftKey && !e.metaKey && !e.ctrlKey; const isModifierEnter = e.key === 'Enter' && (e.metaKey || e.ctrlKey); @@ -1255,6 +1312,19 @@ export class AskView extends LitElement { if (changedProperties.has('showTextInput') || changedProperties.has('isLoading')) { this.adjustWindowHeightThrottled(); } + + if (changedProperties.has('showTextInput') && this.showTextInput) { + this.focusTextInput(); + } + } + + focusTextInput(){ + requestAnimationFrame(() => { + const textInput = this.shadowRoot?.getElementById('textInput'); + if (textInput){ + textInput.focus(); + } + }); } firstUpdated() { @@ -1378,9 +1448,9 @@ export class AskView extends LitElement { const responseHeight = responseEl.scrollHeight; const inputHeight = (inputEl && !inputEl.classList.contains('hidden')) ? inputEl.offsetHeight : 0; - const idealHeight = headerHeight + responseHeight + inputHeight + 20; // padding + const idealHeight = headerHeight + responseHeight + inputHeight; - const targetHeight = Math.min(700, Math.max(200, idealHeight)); + const targetHeight = Math.min(700, idealHeight); const { ipcRenderer } = window.require('electron'); ipcRenderer.invoke('adjust-window-height', targetHeight); diff --git a/src/features/ask/askService.js b/src/features/ask/askService.js new file mode 100644 index 0000000..b59b65d --- /dev/null +++ b/src/features/ask/askService.js @@ -0,0 +1,144 @@ +const { ipcMain, BrowserWindow } = require('electron'); +const { createStreamingLLM } = require('../../common/ai/factory'); +const { getStoredApiKey, getStoredProvider, getCurrentModelInfo, windowPool, captureScreenshot } = require('../../electron/windowManager'); +const authService = require('../../common/services/authService'); +const sessionRepository = require('../../common/repositories/session'); +const askRepository = require('./repositories'); +const { getSystemPrompt } = require('../../common/prompts/promptBuilder'); + +function formatConversationForPrompt(conversationTexts) { + if (!conversationTexts || conversationTexts.length === 0) return 'No conversation history available.'; + return conversationTexts.slice(-30).join('\n'); +} + +// Access conversation history via the global listenService instance created in index.js +function getConversationHistory() { + const listenService = global.listenService; + return listenService ? listenService.getConversationHistory() : []; +} + +async function sendMessage(userPrompt) { + if (!userPrompt || userPrompt.trim().length === 0) { + console.warn('[AskService] Cannot process empty message'); + return { success: false, error: 'Empty message' }; + } + + const askWindow = windowPool.get('ask'); + if (askWindow && !askWindow.isDestroyed()) { + askWindow.webContents.send('hide-text-input'); + } + + try { + console.log(`[AskService] πŸ€– Processing message: ${userPrompt.substring(0, 50)}...`); + + const modelInfo = await getCurrentModelInfo(null, { type: 'llm' }); + if (!modelInfo || !modelInfo.apiKey) { + throw new Error('AI model or API key not configured.'); + } + console.log(`[AskService] Using model: ${modelInfo.model} for provider: ${modelInfo.provider}`); + + const screenshotResult = await captureScreenshot({ quality: 'medium' }); + const screenshotBase64 = screenshotResult.success ? screenshotResult.base64 : null; + + const conversationHistoryRaw = getConversationHistory(); + const conversationHistory = formatConversationForPrompt(conversationHistoryRaw); + + const systemPrompt = getSystemPrompt('pickle_glass_analysis', conversationHistory, false); + + + const messages = [ + { role: 'system', content: systemPrompt }, + { + role: 'user', + content: [ + { type: 'text', text: `User Request: ${userPrompt.trim()}` }, + ], + }, + ]; + + if (screenshotBase64) { + messages[1].content.push({ + type: 'image_url', + image_url: { url: `data:image/jpeg;base64,${screenshotBase64}` }, + }); + } + + const streamingLLM = createStreamingLLM(modelInfo.provider, { + apiKey: modelInfo.apiKey, + model: modelInfo.model, + temperature: 0.7, + maxTokens: 2048, + usePortkey: modelInfo.provider === 'openai-glass', + portkeyVirtualKey: modelInfo.provider === 'openai-glass' ? modelInfo.apiKey : undefined, + }); + + const response = await streamingLLM.streamChat(messages); + + // --- Stream Processing --- + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let fullResponse = ''; + + const askWin = windowPool.get('ask'); + if (!askWin || askWin.isDestroyed()) { + console.error("[AskService] Ask window is not available to send stream to."); + reader.cancel(); + return; + } + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value); + const lines = chunk.split('\n').filter(line => line.trim() !== ''); + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = line.substring(6); + if (data === '[DONE]') { + askWin.webContents.send('ask-response-stream-end'); + + // Save to DB + try { + const uid = authService.getCurrentUserId(); + if (!uid) throw new Error("User not logged in, cannot save message."); + const sessionId = await sessionRepository.getOrCreateActive(uid, 'ask'); + await askRepository.addAiMessage({ sessionId, role: 'user', content: userPrompt.trim() }); + await askRepository.addAiMessage({ sessionId, role: 'assistant', content: fullResponse }); + console.log(`[AskService] DB: Saved ask/answer pair to session ${sessionId}`); + } catch(dbError) { + console.error("[AskService] DB: Failed to save ask/answer pair:", dbError); + } + + return { success: true, response: fullResponse }; + } + try { + const json = JSON.parse(data); + const token = json.choices[0]?.delta?.content || ''; + if (token) { + fullResponse += token; + askWin.webContents.send('ask-response-chunk', { token }); + } + } catch (error) { + // Ignore parsing errors for now + } + } + } + } + } catch (error) { + console.error('[AskService] Error processing message:', error); + return { success: false, error: error.message }; + } +} + +function initialize() { + ipcMain.handle('ask:sendMessage', async (event, userPrompt) => { + return sendMessage(userPrompt); + }); + console.log('[AskService] Initialized and ready.'); +} + +module.exports = { + initialize, +}; \ No newline at end of file diff --git a/src/features/ask/repositories/index.js b/src/features/ask/repositories/index.js new file mode 100644 index 0000000..182d066 --- /dev/null +++ b/src/features/ask/repositories/index.js @@ -0,0 +1,18 @@ +const sqliteRepository = require('./sqlite.repository'); +// const firebaseRepository = require('./firebase.repository'); // Future implementation +const authService = require('../../../common/services/authService'); + +function getRepository() { + // In the future, we can check the user's login status from authService + // const user = authService.getCurrentUser(); + // if (user.isLoggedIn) { + // return firebaseRepository; + // } + return sqliteRepository; +} + +// Directly export functions for ease of use, decided by the strategy +module.exports = { + addAiMessage: (...args) => getRepository().addAiMessage(...args), + getAllAiMessagesBySessionId: (...args) => getRepository().getAllAiMessagesBySessionId(...args), +}; \ No newline at end of file diff --git a/src/features/ask/repositories/sqlite.repository.js b/src/features/ask/repositories/sqlite.repository.js new file mode 100644 index 0000000..bbab3fb --- /dev/null +++ b/src/features/ask/repositories/sqlite.repository.js @@ -0,0 +1,27 @@ +const sqliteClient = require('../../../common/services/sqliteClient'); + +function addAiMessage({ sessionId, role, content, model = 'gpt-4.1' }) { + const db = sqliteClient.getDb(); + const messageId = require('crypto').randomUUID(); + const now = Math.floor(Date.now() / 1000); + const query = `INSERT INTO ai_messages (id, session_id, sent_at, role, content, model, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)`; + + try { + db.prepare(query).run(messageId, sessionId, now, role, content, model, now); + return { id: messageId }; + } catch (err) { + console.error('SQLite: Failed to add AI message:', err); + throw err; + } +} + +function getAllAiMessagesBySessionId(sessionId) { + const db = sqliteClient.getDb(); + const query = "SELECT * FROM ai_messages WHERE session_id = ? ORDER BY sent_at ASC"; + return db.prepare(query).all(sessionId); +} + +module.exports = { + addAiMessage, + getAllAiMessagesBySessionId +}; \ No newline at end of file diff --git a/src/features/customize/CustomizeView.js b/src/features/customize/CustomizeView.js deleted file mode 100644 index fcebfc6..0000000 --- a/src/features/customize/CustomizeView.js +++ /dev/null @@ -1,1127 +0,0 @@ -import { html, css, LitElement } from '../../assets/lit-core-2.7.4.min.js'; - -export class CustomizeView extends LitElement { - static styles = css` - * { - font-family: 'Helvetica Neue', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - cursor: default; - user-select: none; - } - - :host { - display: block; - width: 180px; - min-height: 180px; - color: white; - } - - .settings-container { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - background: rgba(20, 20, 20, 0.8); - border-radius: 12px; - outline: 0.5px rgba(255, 255, 255, 0.2) solid; - outline-offset: -1px; - box-sizing: border-box; - position: relative; - overflow-y: auto; - padding: 12px 12px; - z-index: 1000; - } - - .settings-container::-webkit-scrollbar { - width: 6px; - } - - .settings-container::-webkit-scrollbar-track { - background: rgba(255, 255, 255, 0.05); - border-radius: 3px; - } - - .settings-container::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.2); - border-radius: 3px; - } - - .settings-container::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.3); - } - - .settings-container::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.15); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); - border-radius: 12px; - filter: blur(10px); - z-index: -1; - } - - .settings-button[disabled], - .api-key-section input[disabled] { - opacity: 0.4; - cursor: not-allowed; - pointer-events: none; - } - - .header-section { - display: flex; - justify-content: space-between; - align-items: flex-start; - padding-bottom: 6px; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - position: relative; - z-index: 1; - } - - .title-line { - display: flex; - justify-content: space-between; - align-items: center; - } - - .app-title { - font-size: 13px; - font-weight: 500; - color: white; - margin: 0 0 4px 0; - } - - .account-info { - font-size: 11px; - color: rgba(255, 255, 255, 0.7); - margin: 0; - } - - .invisibility-icon { - padding-top: 2px; - opacity: 0; - transition: opacity 0.3s ease; - } - - .invisibility-icon.visible { - opacity: 1; - } - - .invisibility-icon svg { - width: 16px; - height: 16px; - } - - .shortcuts-section { - display: flex; - flex-direction: column; - gap: 2px; - padding: 4px 0; - position: relative; - z-index: 1; - } - - .shortcut-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 4px 0; - color: white; - font-size: 11px; - } - - .shortcut-name { - font-weight: 300; - } - - .shortcut-keys { - display: flex; - align-items: center; - gap: 3px; - } - - .cmd-key, .shortcut-key { - background: rgba(255, 255, 255, 0.1); - // border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 3px; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - font-size: 11px; - font-weight: 500; - color: rgba(255, 255, 255, 0.9); - } - - /* Buttons Section */ - .buttons-section { - display: flex; - flex-direction: column; - gap: 4px; - padding-top: 6px; - border-top: 1px solid rgba(255, 255, 255, 0.1); - position: relative; - z-index: 1; - flex: 1; - } - - .settings-button { - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 4px; - color: white; - padding: 5px 10px; - font-size: 11px; - font-weight: 400; - cursor: pointer; - transition: all 0.15s ease; - display: flex; - align-items: center; - justify-content: center; - white-space: nowrap; - } - - .settings-button:hover { - background: rgba(255, 255, 255, 0.15); - border-color: rgba(255, 255, 255, 0.3); - } - - .settings-button:active { - transform: translateY(1px); - } - - .settings-button.full-width { - width: 100%; - } - - .settings-button.half-width { - flex: 1; - } - - .settings-button.danger { - background: rgba(255, 59, 48, 0.1); - border-color: rgba(255, 59, 48, 0.3); - color: rgba(255, 59, 48, 0.9); - } - - .settings-button.danger:hover { - background: rgba(255, 59, 48, 0.15); - border-color: rgba(255, 59, 48, 0.4); - } - - .move-buttons, .bottom-buttons { - display: flex; - gap: 4px; - } - - .api-key-section { - padding: 6px 0; - border-top: 1px solid rgba(255, 255, 255, 0.1); - } - - .api-key-section input { - width: 100%; - background: rgba(0,0,0,0.2); - border: 1px solid rgba(255,255,255,0.2); - color: white; - border-radius: 4px; - padding: 4px; - font-size: 11px; - margin-bottom: 4px; - } - - `; - - static properties = { - selectedProfile: { type: String }, - selectedLanguage: { type: String }, - selectedScreenshotInterval: { type: String }, - selectedImageQuality: { type: String }, - layoutMode: { type: String }, - keybinds: { type: Object }, - throttleTokens: { type: Number }, - maxTokens: { type: Number }, - throttlePercent: { type: Number }, - googleSearchEnabled: { type: Boolean }, - backgroundTransparency: { type: Number }, - fontSize: { type: Number }, - onProfileChange: { type: Function }, - onLanguageChange: { type: Function }, - onScreenshotIntervalChange: { type: Function }, - onImageQualityChange: { type: Function }, - onLayoutModeChange: { type: Function }, - contentProtection: { type: Boolean }, - userPresets: { type: Array }, - presetTemplates: { type: Array }, - currentUser: { type: String }, - isContentProtectionOn: { type: Boolean }, - firebaseUser: { type: Object, state: true }, - apiKey: { type: String, state: true }, - isLoading: { type: Boolean }, - activeTab: { type: String }, - }; - - constructor() { - super(); - - this.selectedProfile = localStorage.getItem('selectedProfile') || 'school'; - - // Language format migration for legacy users - let lang = localStorage.getItem('selectedLanguage') || 'en'; - if (lang.includes('-')) { - const newLang = lang.split('-')[0]; - console.warn(`[Migration] Correcting language format from "${lang}" to "${newLang}".`); - localStorage.setItem('selectedLanguage', newLang); - lang = newLang; - } - this.selectedLanguage = lang; - - this.selectedScreenshotInterval = localStorage.getItem('selectedScreenshotInterval') || '5000'; - this.selectedImageQuality = localStorage.getItem('selectedImageQuality') || '0.8'; - this.layoutMode = localStorage.getItem('layoutMode') || 'stacked'; - this.keybinds = this.getDefaultKeybinds(); - this.throttleTokens = 500; - this.maxTokens = 2000; - this.throttlePercent = 80; - this.backgroundTransparency = 0.5; - this.fontSize = 14; - this.userPresets = []; - this.presetTemplates = []; - this.currentUser = 'default_user'; - this.firebaseUser = null; - this.apiKey = null; - this.isContentProtectionOn = true; - this.isLoading = false; - this.activeTab = 'prompts'; - - this.loadKeybinds(); - this.loadRateLimitSettings(); - this.loadGoogleSearchSettings(); - this.loadBackgroundTransparency(); - this.loadFontSize(); - this.loadContentProtectionSettings(); - this.checkContentProtectionStatus(); - this.getApiKeyFromStorage(); - } - - connectedCallback() { - super.connectedCallback(); - - this.loadLayoutMode(); - this.loadInitialData(); - - this.resizeHandler = () => { - this.requestUpdate(); - this.updateScrollHeight(); - }; - window.addEventListener('resize', this.resizeHandler); - - setTimeout(() => this.updateScrollHeight(), 100); - - this.addEventListener('mouseenter', () => { - if (window.require) { - window.require('electron').ipcRenderer.send('cancel-hide-window', 'settings'); - } - }); - - this.addEventListener('mouseleave', () => { - if (window.require) { - window.require('electron').ipcRenderer.send('hide-window', 'settings'); - } - }); - - if (window.require) { - const { ipcRenderer } = window.require('electron'); - - ipcRenderer.on('firebase-user-updated', (event, user) => { - this.firebaseUser = user; - - if (!user) { - this.apiKey = null; - } - - this.requestUpdate(); - }); - - ipcRenderer.on('user-changed', (event, firebaseUser) => { - console.log('[CustomizeView] Received user-changed:', firebaseUser); - this.firebaseUser = { - uid: firebaseUser.uid, - email: firebaseUser.email, - name: firebaseUser.displayName, - photoURL: firebaseUser.photoURL, - }; - this.requestUpdate(); - }); - - ipcRenderer.on('api-key-validated', (event, newApiKey) => { - console.log('[CustomizeView] Received api-key-validated, updating state.'); - this.apiKey = newApiKey; - this.requestUpdate(); - }); - - ipcRenderer.on('api-key-updated', () => { - console.log('[CustomizeView] Received api-key-updated, refreshing state.'); - this.getApiKeyFromStorage(); - }); - - ipcRenderer.on('api-key-removed', () => { - console.log('[CustomizeView] Received api-key-removed, clearing state.'); - this.apiKey = null; - this.requestUpdate(); - }); - - this.loadInitialFirebaseUser(); - - ipcRenderer.invoke('get-current-api-key').then(key => { - this.apiKey = key; - this.requestUpdate(); - }); - } - } - - disconnectedCallback() { - super.disconnectedCallback(); - if (this.resizeHandler) { - window.removeEventListener('resize', this.resizeHandler); - } - - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeAllListeners('firebase-user-updated'); - ipcRenderer.removeAllListeners('user-changed'); - ipcRenderer.removeAllListeners('api-key-validated'); - ipcRenderer.removeAllListeners('api-key-updated'); - ipcRenderer.removeAllListeners('api-key-removed'); - } - } - - updateScrollHeight() { - const windowHeight = window.innerHeight; - const headerHeight = 60; - const padding = 40; - const maxHeight = windowHeight - headerHeight - padding; - - this.style.maxHeight = `${maxHeight}px`; - } - - async checkContentProtectionStatus() { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - this.isContentProtectionOn = await ipcRenderer.invoke('get-content-protection-status'); - this.requestUpdate(); - } - } - - getProfiles() { - if (this.presetTemplates && this.presetTemplates.length > 0) { - return this.presetTemplates.map(t => ({ - value: t.id || t._id, - name: t.title, - description: t.prompt?.slice(0, 60) + '...', - })); - } - - return [ - { value: 'school', name: 'School', description: '' }, - { value: 'meetings', name: 'Meetings', description: '' }, - { value: 'sales', name: 'Sales', description: '' }, - { value: 'recruiting', name: 'Recruiting', description: '' }, - { value: 'customer-support', name: 'Customer Support', description: '' }, - ]; - } - - getLanguages() { - return [ - { value: 'en', name: 'English' }, - { value: 'de', name: 'German' }, - { value: 'es', name: 'Spanish' }, - { value: 'fr', name: 'French' }, - { value: 'hi', name: 'Hindi' }, - { value: 'pt', name: 'Portuguese' }, - { value: 'ar', name: 'Arabic' }, - { value: 'id', name: 'Indonesian' }, - { value: 'it', name: 'Italian' }, - { value: 'ja', name: 'Japanese' }, - { value: 'tr', name: 'Turkish' }, - { value: 'vi', name: 'Vietnamese' }, - { value: 'bn', name: 'Bengali' }, - { value: 'gu', name: 'Gujarati' }, - { value: 'kn', name: 'Kannada' }, - { value: 'ml', name: 'Malayalam' }, - { value: 'mr', name: 'Marathi' }, - { value: 'ta', name: 'Tamil' }, - { value: 'te', name: 'Telugu' }, - { value: 'nl', name: 'Dutch' }, - { value: 'ko', name: 'Korean' }, - { value: 'zh', name: 'Chinese' }, - { value: 'pl', name: 'Polish' }, - { value: 'ru', name: 'Russian' }, - { value: 'th', name: 'Thai' }, - ]; - } - - getProfileNames() { - return { - interview: 'Job Interview', - sales: 'Sales Call', - meeting: 'Business Meeting', - presentation: 'Presentation', - negotiation: 'Negotiation', - }; - } - - handleProfileSelect(e) { - this.selectedProfile = e.target.value; - localStorage.setItem('selectedProfile', this.selectedProfile); - this.onProfileChange(this.selectedProfile); - } - - handleLanguageSelect(e) { - this.selectedLanguage = e.target.value; - localStorage.setItem('selectedLanguage', this.selectedLanguage); - this.onLanguageChange(this.selectedLanguage); - } - - handleScreenshotIntervalSelect(e) { - this.selectedScreenshotInterval = e.target.value; - localStorage.setItem('selectedScreenshotInterval', this.selectedScreenshotInterval); - this.onScreenshotIntervalChange(this.selectedScreenshotInterval); - } - - handleImageQualitySelect(e) { - this.selectedImageQuality = e.target.value; - this.onImageQualityChange(e.target.value); - } - - handleLayoutModeSelect(e) { - this.layoutMode = e.target.value; - localStorage.setItem('layoutMode', this.layoutMode); - this.onLayoutModeChange(e.target.value); - } - - getUserCustomPrompt() { - console.log('[CustomizeView] getUserCustomPrompt called'); - console.log('[CustomizeView] userPresets:', this.userPresets); - console.log('[CustomizeView] selectedProfile:', this.selectedProfile); - - if (!this.userPresets || this.userPresets.length === 0) { - console.log('[CustomizeView] No presets - returning loading message'); - return 'Loading personalized prompt... Please set it in the web.'; - } - - let preset = this.userPresets.find(p => p.id === 'personalized' || p._id === 'personalized'); - console.log('[CustomizeView] personalized preset:', preset); - - if (!preset) { - preset = this.userPresets.find(p => p.id === this.selectedProfile || p._id === this.selectedProfile); - console.log('[CustomizeView] selectedProfile preset:', preset); - } - - if (!preset) { - preset = this.userPresets[0]; - console.log('[CustomizeView] Using first preset:', preset); - } - - const result = preset?.prompt || 'No personalized prompt set.'; - console.log('[CustomizeView] Final returned prompt:', result); - return result; - } - - async loadInitialData() { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - try { - this.isLoading = true; - this.userPresets = await ipcRenderer.invoke('get-user-presets'); - this.presetTemplates = await ipcRenderer.invoke('get-preset-templates'); - console.log('[CustomizeView] Loaded presets and templates via IPC'); - } catch (error) { - console.error('[CustomizeView] Failed to load data via IPC:', error); - } finally { - this.isLoading = false; - } - } else { - console.log('[CustomizeView] IPC not available'); - } - } - - getDefaultKeybinds() { - const isMac = window.pickleGlass?.isMacOS || navigator.platform.includes('Mac'); - return { - moveUp: isMac ? 'Cmd+Up' : 'Ctrl+Up', - moveDown: isMac ? 'Cmd+Down' : 'Ctrl+Down', - moveLeft: isMac ? 'Cmd+Left' : 'Ctrl+Left', - moveRight: isMac ? 'Cmd+Right' : 'Ctrl+Right', - toggleVisibility: isMac ? 'Cmd+\\' : 'Ctrl+\\', - toggleClickThrough: isMac ? 'Cmd+M' : 'Ctrl+M', - nextStep: isMac ? 'Cmd+Enter' : 'Ctrl+Enter', - manualScreenshot: isMac ? 'Cmd+Shift+S' : 'Ctrl+Shift+S', - previousResponse: isMac ? 'Cmd+[' : 'Ctrl+[', - nextResponse: isMac ? 'Cmd+]' : 'Ctrl+]', - scrollUp: isMac ? 'Cmd+Shift+Up' : 'Ctrl+Shift+Up', - scrollDown: isMac ? 'Cmd+Shift+Down' : 'Ctrl+Shift+Down', - }; - } - - loadKeybinds() { - const savedKeybinds = localStorage.getItem('customKeybinds'); - if (savedKeybinds) { - try { - this.keybinds = { ...this.getDefaultKeybinds(), ...JSON.parse(savedKeybinds) }; - } catch (e) { - console.error('Failed to parse saved keybinds:', e); - this.keybinds = this.getDefaultKeybinds(); - } - } - } - - saveKeybinds() { - localStorage.setItem('customKeybinds', JSON.stringify(this.keybinds)); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.send('update-keybinds', this.keybinds); - } - } - - handleKeybindChange(action, value) { - this.keybinds = { ...this.keybinds, [action]: value }; - this.saveKeybinds(); - this.requestUpdate(); - } - - resetKeybinds() { - this.keybinds = this.getDefaultKeybinds(); - localStorage.removeItem('customKeybinds'); - this.requestUpdate(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.send('update-keybinds', this.keybinds); - } - } - - getKeybindActions() { - return [ - { - key: 'moveUp', - name: 'Move Window Up', - description: 'Move the application window up', - }, - { - key: 'moveDown', - name: 'Move Window Down', - description: 'Move the application window down', - }, - { - key: 'moveLeft', - name: 'Move Window Left', - description: 'Move the application window left', - }, - { - key: 'moveRight', - name: 'Move Window Right', - description: 'Move the application window right', - }, - { - key: 'toggleVisibility', - name: 'Toggle Window Visibility', - description: 'Show/hide the application window', - }, - { - key: 'toggleClickThrough', - name: 'Toggle Click-through Mode', - description: 'Enable/disable click-through functionality', - }, - { - key: 'nextStep', - name: 'Ask Next Step', - description: 'Ask AI for the next step suggestion', - }, - { - key: 'manualScreenshot', - name: 'Manual Screenshot', - description: 'Take a manual screenshot for AI analysis', - }, - { - key: 'previousResponse', - name: 'Previous Response', - description: 'Navigate to the previous AI response', - }, - { - key: 'nextResponse', - name: 'Next Response', - description: 'Navigate to the next AI response', - }, - { - key: 'scrollUp', - name: 'Scroll Response Up', - description: 'Scroll the AI response content up', - }, - { - key: 'scrollDown', - name: 'Scroll Response Down', - description: 'Scroll the AI response content down', - }, - ]; - } - - handleKeybindFocus(e) { - e.target.placeholder = 'Press key combination...'; - e.target.select(); - } - - handleKeybindInput(e) { - e.preventDefault(); - - const modifiers = []; - const keys = []; - - if (e.ctrlKey) modifiers.push('Ctrl'); - if (e.metaKey) modifiers.push('Cmd'); - if (e.altKey) modifiers.push('Alt'); - if (e.shiftKey) modifiers.push('Shift'); - - let mainKey = e.key; - - switch (e.code) { - case 'ArrowUp': - mainKey = 'Up'; - break; - case 'ArrowDown': - mainKey = 'Down'; - break; - case 'ArrowLeft': - mainKey = 'Left'; - break; - case 'ArrowRight': - mainKey = 'Right'; - break; - case 'Enter': - mainKey = 'Enter'; - break; - case 'Space': - mainKey = 'Space'; - break; - case 'Backslash': - mainKey = '\\'; - break; - case 'KeyS': - if (e.shiftKey) mainKey = 'S'; - break; - case 'KeyM': - mainKey = 'M'; - break; - default: - if (e.key.length === 1) { - mainKey = e.key.toUpperCase(); - } - break; - } - - if (['Control', 'Meta', 'Alt', 'Shift'].includes(e.key)) { - return; - } - - const keybind = [...modifiers, mainKey].join('+'); - - const action = e.target.dataset.action; - - this.handleKeybindChange(action, keybind); - - e.target.value = keybind; - e.target.blur(); - } - - loadRateLimitSettings() { - const throttleTokens = localStorage.getItem('throttleTokens'); - const maxTokens = localStorage.getItem('maxTokens'); - const throttlePercent = localStorage.getItem('throttlePercent'); - - if (throttleTokens !== null) { - this.throttleTokens = parseInt(throttleTokens, 10) || 500; - } - if (maxTokens !== null) { - this.maxTokens = parseInt(maxTokens, 10) || 2000; - } - if (throttlePercent !== null) { - this.throttlePercent = parseInt(throttlePercent, 10) || 80; - } - } - - handleThrottleTokensChange(e) { - this.throttleTokens = parseInt(e.target.value, 10); - localStorage.setItem('throttleTokens', this.throttleTokens.toString()); - this.requestUpdate(); - } - - handleMaxTokensChange(e) { - const value = parseInt(e.target.value, 10); - if (!isNaN(value) && value > 0) { - this.maxTokens = value; - localStorage.setItem('maxTokens', this.maxTokens.toString()); - } - } - - handleThrottlePercentChange(e) { - const value = parseInt(e.target.value, 10); - if (!isNaN(value) && value >= 0 && value <= 100) { - this.throttlePercent = value; - localStorage.setItem('throttlePercent', this.throttlePercent.toString()); - } - } - - resetRateLimitSettings() { - this.throttleTokens = 500; - this.maxTokens = 2000; - this.throttlePercent = 80; - - localStorage.removeItem('throttleTokens'); - localStorage.removeItem('maxTokens'); - localStorage.removeItem('throttlePercent'); - - this.requestUpdate(); - } - - loadGoogleSearchSettings() { - const googleSearchEnabled = localStorage.getItem('googleSearchEnabled'); - if (googleSearchEnabled !== null) { - this.googleSearchEnabled = googleSearchEnabled === 'true'; - } - } - - async handleGoogleSearchChange(e) { - this.googleSearchEnabled = e.target.checked; - localStorage.setItem('googleSearchEnabled', this.googleSearchEnabled.toString()); - - if (window.require) { - try { - const { ipcRenderer } = window.require('electron'); - await ipcRenderer.invoke('update-google-search-setting', this.googleSearchEnabled); - } catch (error) { - console.error('Failed to notify main process:', error); - } - } - - this.requestUpdate(); - } - - loadLayoutMode() { - const savedLayoutMode = localStorage.getItem('layoutMode'); - if (savedLayoutMode) { - this.layoutMode = savedLayoutMode; - } - } - - loadBackgroundTransparency() { - const backgroundTransparency = localStorage.getItem('backgroundTransparency'); - if (backgroundTransparency !== null) { - this.backgroundTransparency = parseFloat(backgroundTransparency) || 0.5; - } - this.updateBackgroundTransparency(); - } - - handleBackgroundTransparencyChange(e) { - this.backgroundTransparency = parseFloat(e.target.value); - localStorage.setItem('backgroundTransparency', this.backgroundTransparency.toString()); - this.updateBackgroundTransparency(); - this.requestUpdate(); - } - - updateBackgroundTransparency() { - const root = document.documentElement; - root.style.setProperty('--header-background', `rgba(0, 0, 0, ${this.backgroundTransparency})`); - root.style.setProperty('--main-content-background', `rgba(0, 0, 0, ${this.backgroundTransparency})`); - root.style.setProperty('--card-background', `rgba(255, 255, 255, ${this.backgroundTransparency * 0.05})`); - root.style.setProperty('--input-background', `rgba(0, 0, 0, ${this.backgroundTransparency * 0.375})`); - root.style.setProperty('--input-focus-background', `rgba(0, 0, 0, ${this.backgroundTransparency * 0.625})`); - root.style.setProperty('--button-background', `rgba(0, 0, 0, ${this.backgroundTransparency * 0.625})`); - root.style.setProperty('--preview-video-background', `rgba(0, 0, 0, ${this.backgroundTransparency * 1.125})`); - root.style.setProperty('--screen-option-background', `rgba(0, 0, 0, ${this.backgroundTransparency * 0.5})`); - root.style.setProperty('--screen-option-hover-background', `rgba(0, 0, 0, ${this.backgroundTransparency * 0.75})`); - root.style.setProperty('--scrollbar-background', `rgba(0, 0, 0, ${this.backgroundTransparency * 0.5})`); - } - - loadFontSize() { - const fontSize = localStorage.getItem('fontSize'); - if (fontSize !== null) { - this.fontSize = parseInt(fontSize, 10) || 14; - } - this.updateFontSize(); - } - - handleFontSizeChange(e) { - this.fontSize = parseInt(e.target.value, 10); - localStorage.setItem('fontSize', this.fontSize.toString()); - this.updateFontSize(); - this.requestUpdate(); - } - - updateFontSize() { - const root = document.documentElement; - root.style.setProperty('--response-font-size', `${this.fontSize}px`); - } - - loadContentProtectionSettings() { - const contentProtection = localStorage.getItem('contentProtection'); - if (contentProtection !== null) { - this.contentProtection = contentProtection === 'true'; - } - } - - async handleContentProtectionChange(e) { - this.contentProtection = e.target.checked; - localStorage.setItem('contentProtection', this.contentProtection.toString()); - - if (window.require) { - try { - const { ipcRenderer } = window.require('electron'); - await ipcRenderer.invoke('update-content-protection', this.contentProtection); - } catch (error) { - console.error('Failed to notify main process about content protection change:', error); - } - } - - this.requestUpdate(); - } - - render() { - const loggedIn = !!this.firebaseUser; - console.log('[CustomizeView] render: Rendering component template.'); - return html` -
-
-
-

Pickle Glass

- -
-
- - - -
-
- -
- - -
- -
- ${this.getMainShortcuts().map(shortcut => html` -
- ${shortcut.name} -
- ⌘ - ${shortcut.key} -
-
- `)} -
- -
- - -
- - -
- - - -
- ${this.firebaseUser - ? html` - - ` - : html` - - ` - } - -
-
-
- `; - } - - getMainShortcuts() { - return [ - { name: 'Show / Hide', key: '\\' }, - { name: 'Ask Anything', key: '↡' }, - { name: 'Scroll AI Response', key: '↕' } - ]; - } - - handleMoveLeft() { - console.log('Move Left clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('move-window-step', 'left'); - } - } - - handleMoveRight() { - console.log('Move Right clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('move-window-step', 'right'); - } - } - - async handlePersonalize() { - console.log('Personalize clicked'); - if (window.require) { - const { ipcRenderer, shell } = window.require('electron'); - try { - const webUrl = await ipcRenderer.invoke('get-web-url'); - shell.openExternal(`${webUrl}/personalize`); - } catch (error) { - console.error('Failed to get web URL or open external link:', error); - shell.openExternal('http://localhost:3000/personalize'); - } - } - } - - async handleToggleInvisibility() { - console.log('Toggle Invisibility clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - this.isContentProtectionOn = await ipcRenderer.invoke('toggle-content-protection'); - this.requestUpdate(); - } - } - - async handleSaveApiKey() { - const input = this.shadowRoot.getElementById('api-key-input'); - if (!input || !input.value) return; - - const newApiKey = input.value; - if (window.require) { - const { ipcRenderer } = window.require('electron'); - try { - const result = await ipcRenderer.invoke('save-api-key', newApiKey); - if (result.success) { - console.log('API Key saved successfully via IPC.'); - this.apiKey = newApiKey; - this.requestUpdate(); - } else { - console.error('Failed to save API Key via IPC:', result.error); - } - } catch(e) { - console.error('Error invoking save-api-key IPC:', e); - } - } - } - - async handleClearApiKey() { - console.log('Clear API Key clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - await ipcRenderer.invoke('remove-api-key'); - this.requestUpdate(); - } - } - - handleQuit() { - console.log('Quit clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('quit-application'); - } - } - - handleFirebaseLogout() { - console.log('Firebase Logout clicked'); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('firebase-logout'); - } - } - - async loadInitialFirebaseUser() { - if (!window.require) { - console.log('[CustomizeView] Electron not available'); - return; - } - - const { ipcRenderer } = window.require('electron'); - - try { - console.log('[CustomizeView] Loading initial Firebase user...'); - - for (let i = 0; i < 3; i++) { - const user = await ipcRenderer.invoke('get-current-firebase-user'); - console.log(`[CustomizeView] Attempt ${i + 1} - Firebase user:`, user); - - if (user) { - this.firebaseUser = user; - this.requestUpdate(); - console.log('[CustomizeView] Firebase user loaded successfully:', user.email); - return; - } - - await new Promise(resolve => setTimeout(resolve, 100)); - } - - console.log('[CustomizeView] No Firebase user found after 3 attempts'); - this.firebaseUser = null; - this.requestUpdate(); - - } catch (error) { - console.error('[CustomizeView] Failed to load Firebase user:', error); - this.firebaseUser = null; - this.requestUpdate(); - } - } - - getApiKeyFromStorage() { - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.invoke('get-current-api-key').then(key => { - this.apiKey = key; - this.requestUpdate(); - }).catch(error => { - console.log('[CustomizeView] Failed to get API key:', error); - this.apiKey = null; - }); - } - return null; - } -} - -customElements.define('customize-view', CustomizeView); diff --git a/src/features/listen/AssistantView.js b/src/features/listen/AssistantView.js index aa8d279..9c6a7e9 100644 --- a/src/features/listen/AssistantView.js +++ b/src/features/listen/AssistantView.js @@ -1,4 +1,6 @@ import { html, css, LitElement } from '../../assets/lit-core-2.7.4.min.js'; +import './stt/SttView.js'; +import './summary/SummaryView.js'; export class AssistantView extends LitElement { static styles = css` @@ -82,79 +84,87 @@ export class AssistantView extends LitElement { user-select: none; } - /* Allow text selection in insights responses */ - .insights-container, .insights-container *, .markdown-content { - user-select: text !important; - cursor: text !important; - } +/* Allow text selection in insights responses */ +.insights-container, .insights-container *, .markdown-content { + user-select: text !important; + cursor: text !important; +} - /* highlight.js μŠ€νƒ€μΌ μΆ”κ°€ */ - .insights-container pre { - background: rgba(0, 0, 0, 0.4) !important; - border-radius: 8px !important; - padding: 12px !important; - margin: 8px 0 !important; - overflow-x: auto !important; - border: 1px solid rgba(255, 255, 255, 0.1) !important; - white-space: pre !important; - word-wrap: normal !important; - word-break: normal !important; - } +/* highlight.js μŠ€νƒ€μΌ μΆ”κ°€ */ +.insights-container pre { + background: rgba(0, 0, 0, 0.4) !important; + border-radius: 8px !important; + padding: 12px !important; + margin: 8px 0 !important; + overflow-x: auto !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; + white-space: pre !important; + word-wrap: normal !important; + word-break: normal !important; +} - .insights-container code { - font-family: 'Monaco', 'Menlo', 'Consolas', monospace !important; - font-size: 11px !important; - background: transparent !important; - white-space: pre !important; - word-wrap: normal !important; - word-break: normal !important; - } +.insights-container code { + font-family: 'Monaco', 'Menlo', 'Consolas', monospace !important; + font-size: 11px !important; + background: transparent !important; + white-space: pre !important; + word-wrap: normal !important; + word-break: normal !important; +} - .insights-container pre code { - white-space: pre !important; - word-wrap: normal !important; - word-break: normal !important; - display: block !important; - } +.insights-container pre code { + white-space: pre !important; + word-wrap: normal !important; + word-break: normal !important; + display: block !important; +} - .insights-container p code { - background: rgba(255, 255, 255, 0.1) !important; - padding: 2px 4px !important; - border-radius: 3px !important; - color: #ffd700 !important; - } +.insights-container p code { + background: rgba(255, 255, 255, 0.1) !important; + padding: 2px 4px !important; + border-radius: 3px !important; + color: #ffd700 !important; +} - .hljs-keyword { - color: #ff79c6 !important; - } - .hljs-string { - color: #f1fa8c !important; - } - .hljs-comment { - color: #6272a4 !important; - } - .hljs-number { - color: #bd93f9 !important; - } - .hljs-function { - color: #50fa7b !important; - } - .hljs-variable { - color: #8be9fd !important; - } - .hljs-built_in { - color: #ffb86c !important; - } - .hljs-title { - color: #50fa7b !important; - } - .hljs-attr { - color: #50fa7b !important; - } - .hljs-tag { - color: #ff79c6 !important; - } +.hljs-keyword { + color: #ff79c6 !important; +} +.hljs-string { + color: #f1fa8c !important; +} + +.hljs-comment { + color: #6272a4 !important; +} + +.hljs-number { + color: #bd93f9 !important; +} + +.hljs-function { + color: #50fa7b !important; +} + +.hljs-title { + color: #50fa7b !important; +} + +.hljs-variable { + color: #8be9fd !important; +} + +.hljs-built_in { + color: #ffb86c !important; +} + +.hljs-attr { + color: #50fa7b !important; +} + +.hljs-tag { + color: #ff79c6 !important; +} .assistant-container { display: flex; flex-direction: column; @@ -164,10 +174,8 @@ export class AssistantView extends LitElement { background: rgba(0, 0, 0, 0.6); overflow: hidden; border-radius: 12px; - /* outline: 0.5px rgba(255, 255, 255, 0.5) solid; */ - /* outline-offset: -1px; */ width: 100%; - min-height: 200px; + height: 100%; } .assistant-container::after { @@ -177,7 +185,7 @@ export class AssistantView extends LitElement { left: 0; right: 0; bottom: 0; - border-radius: 12px; /* Match parent */ + border-radius: 12px; padding: 1px; background: linear-gradient(169deg, rgba(255, 255, 255, 0.17) 0%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.17) 100%); -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); @@ -305,8 +313,8 @@ export class AssistantView extends LitElement { height: 24px; flex-shrink: 0; transition: background-color 0.15s ease; - position: relative; /* For icon positioning */ - overflow: hidden; /* Hide overflowing parts of icons during animation */ + position: relative; + overflow: hidden; } .copy-button:hover { @@ -336,225 +344,142 @@ export class AssistantView extends LitElement { transform: translate(-50%, -50%) scale(1); } - .transcription-container { - overflow-y: auto; - padding: 12px 12px 16px 12px; - display: flex; - flex-direction: column; - gap: 8px; - min-height: 150px; - max-height: 600px; - position: relative; - z-index: 1; - flex: 1; - } - - .transcription-container.hidden { - display: none; - } - - .transcription-container::-webkit-scrollbar { - width: 8px; - } - .transcription-container::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0.1); - border-radius: 4px; - } - .transcription-container::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 4px; - } - .transcription-container::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.5); - } - - .stt-message { - padding: 8px 12px; - border-radius: 12px; - max-width: 80%; - word-wrap: break-word; - word-break: break-word; - line-height: 1.5; - font-size: 13px; - margin-bottom: 4px; - box-sizing: border-box; - } - - .stt-message.them { - background: rgba(255, 255, 255, 0.1); - color: rgba(255, 255, 255, 0.9); - align-self: flex-start; - border-bottom-left-radius: 4px; - margin-right: auto; - } - - .stt-message.me { - background: rgba(0, 122, 255, 0.8); - color: white; - align-self: flex-end; - border-bottom-right-radius: 4px; - margin-left: auto; - } - - .insights-container { - overflow-y: auto; - padding: 12px 16px 16px 16px; - position: relative; - z-index: 1; - min-height: 150px; - max-height: 600px; - flex: 1; - } - - insights-title { - color: rgba(255, 255, 255, 0.8); - font-size: 15px; - font-weight: 500; - font-family: 'Helvetica Neue', sans-serif; - margin: 12px 0 8px 0; - } - - .insights-container.hidden { - display: none; - } - - .insights-container::-webkit-scrollbar { - width: 8px; - } - .insights-container::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0.1); - border-radius: 4px; - } - .insights-container::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.3); - border-radius: 4px; - } - .insights-container::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.5); - } - - .insights-container h4 { - color: #ffffff; - font-size: 12px; - font-weight: 600; - margin: 12px 0 8px 0; - padding: 4px 8px; - border-radius: 4px; - background: transparent; - cursor: default; - } - - .insights-container h4:hover { - background: transparent; - } - - .insights-container h4:first-child { - margin-top: 0; - } - - .outline-item { - color: #ffffff; - font-size: 11px; - line-height: 1.4; - margin: 4px 0; - padding: 6px 8px; - border-radius: 4px; - background: transparent; - transition: background-color 0.15s ease; - cursor: pointer; - word-wrap: break-word; - } - - .outline-item:hover { - background: rgba(255, 255, 255, 0.1); - } - - .request-item { - color: #ffffff; - font-size: 12px; - line-height: 1.2; - margin: 4px 0; - padding: 6px 8px; - border-radius: 4px; - background: transparent; - cursor: default; - word-wrap: break-word; - transition: background-color 0.15s ease; - } - - .request-item.clickable { - cursor: pointer; - transition: all 0.15s ease; - } - .request-item.clickable:hover { - background: rgba(255, 255, 255, 0.1); - transform: translateX(2px); - } - - /* λ§ˆν¬λ‹€μš΄ λ Œλ”λ§λœ μ½˜ν…μΈ  μŠ€νƒ€μΌ */ - .markdown-content { - color: #ffffff; - font-size: 11px; - line-height: 1.4; - margin: 4px 0; - padding: 6px 8px; - border-radius: 4px; - background: transparent; - cursor: pointer; - word-wrap: break-word; - transition: all 0.15s ease; - } - - .markdown-content:hover { - background: rgba(255, 255, 255, 0.1); - transform: translateX(2px); - } - - .markdown-content p { - margin: 4px 0; - } - - .markdown-content ul, - .markdown-content ol { - margin: 4px 0; - padding-left: 16px; - } - - .markdown-content li { - margin: 2px 0; - } - - .markdown-content a { - color: #8be9fd; - text-decoration: none; - } - - .markdown-content a:hover { - text-decoration: underline; - } - - .markdown-content strong { - font-weight: 600; - color: #f8f8f2; - } - - .markdown-content em { - font-style: italic; - color: #f1fa8c; - } - .timer { font-family: 'Monaco', 'Menlo', monospace; font-size: 10px; color: rgba(255, 255, 255, 0.7); } + + /* ────────────────[ GLASS BYPASS ]─────────────── */ + :host-context(body.has-glass) .assistant-container, + :host-context(body.has-glass) .top-bar, + :host-context(body.has-glass) .toggle-button, + :host-context(body.has-glass) .copy-button, + :host-context(body.has-glass) .transcription-container, + :host-context(body.has-glass) .insights-container, + :host-context(body.has-glass) .stt-message, + :host-context(body.has-glass) .outline-item, + :host-context(body.has-glass) .request-item, + :host-context(body.has-glass) .markdown-content, + :host-context(body.has-glass) .insights-container pre, + :host-context(body.has-glass) .insights-container p code, + :host-context(body.has-glass) .insights-container pre code { + background: transparent !important; + border: none !important; + outline: none !important; + box-shadow: none !important; + filter: none !important; + backdrop-filter: none !important; + } + + :host-context(body.has-glass) .assistant-container::before, + :host-context(body.has-glass) .assistant-container::after { + display: none !important; + } + + :host-context(body.has-glass) .toggle-button:hover, + :host-context(body.has-glass) .copy-button:hover, + :host-context(body.has-glass) .outline-item:hover, + :host-context(body.has-glass) .request-item.clickable:hover, + :host-context(body.has-glass) .markdown-content:hover { + background: transparent !important; + transform: none !important; + } + + :host-context(body.has-glass) .transcription-container::-webkit-scrollbar-track, + :host-context(body.has-glass) .transcription-container::-webkit-scrollbar-thumb, + :host-context(body.has-glass) .insights-container::-webkit-scrollbar-track, + :host-context(body.has-glass) .insights-container::-webkit-scrollbar-thumb { + background: transparent !important; + } + :host-context(body.has-glass) * { + animation: none !important; + transition: none !important; + transform: none !important; + filter: none !important; + backdrop-filter: none !important; + box-shadow: none !important; + } + + :host-context(body.has-glass) .assistant-container, + :host-context(body.has-glass) .stt-message, + :host-context(body.has-glass) .toggle-button, + :host-context(body.has-glass) .copy-button { + border-radius: 0 !important; + } + + :host-context(body.has-glass) ::-webkit-scrollbar, + :host-context(body.has-glass) ::-webkit-scrollbar-track, + :host-context(body.has-glass) ::-webkit-scrollbar-thumb { + background: transparent !important; + width: 0 !important; /* μŠ€ν¬λ‘€λ°” 자체 숨기기 */ + } + :host-context(body.has-glass) .assistant-container, + :host-context(body.has-glass) .top-bar, + :host-context(body.has-glass) .toggle-button, + :host-context(body.has-glass) .copy-button, + :host-context(body.has-glass) .transcription-container, + :host-context(body.has-glass) .insights-container, + :host-context(body.has-glass) .stt-message, + :host-context(body.has-glass) .outline-item, + :host-context(body.has-glass) .request-item, + :host-context(body.has-glass) .markdown-content, + :host-context(body.has-glass) .insights-container pre, + :host-context(body.has-glass) .insights-container p code, + :host-context(body.has-glass) .insights-container pre code { + background: transparent !important; + border: none !important; + outline: none !important; + box-shadow: none !important; + filter: none !important; + backdrop-filter: none !important; + } + + :host-context(body.has-glass) .assistant-container::before, + :host-context(body.has-glass) .assistant-container::after { + display: none !important; + } + + :host-context(body.has-glass) .toggle-button:hover, + :host-context(body.has-glass) .copy-button:hover, + :host-context(body.has-glass) .outline-item:hover, + :host-context(body.has-glass) .request-item.clickable:hover, + :host-context(body.has-glass) .markdown-content:hover { + background: transparent !important; + transform: none !important; + } + + :host-context(body.has-glass) .transcription-container::-webkit-scrollbar-track, + :host-context(body.has-glass) .transcription-container::-webkit-scrollbar-thumb, + :host-context(body.has-glass) .insights-container::-webkit-scrollbar-track, + :host-context(body.has-glass) .insights-container::-webkit-scrollbar-thumb { + background: transparent !important; + } + :host-context(body.has-glass) * { + animation: none !important; + transition: none !important; + transform: none !important; + filter: none !important; + backdrop-filter: none !important; + box-shadow: none !important; + } + + :host-context(body.has-glass) .assistant-container, + :host-context(body.has-glass) .stt-message, + :host-context(body.has-glass) .toggle-button, + :host-context(body.has-glass) .copy-button { + border-radius: 0 !important; + } + + :host-context(body.has-glass) ::-webkit-scrollbar, + :host-context(body.has-glass) ::-webkit-scrollbar-track, + :host-context(body.has-glass) ::-webkit-scrollbar-thumb { + background: transparent !important; + width: 0 !important; + } `; static properties = { - structuredData: { type: Object }, - // outlines: { type: Array }, - // analysisRequests: { type: Array }, - sttMessages: { type: Array }, viewMode: { type: String }, isHovering: { type: Boolean }, isAnimating: { type: Boolean }, @@ -567,183 +492,66 @@ export class AssistantView extends LitElement { constructor() { super(); - // this.outlines = []; - // this.analysisRequests = []; - this.structuredData = { - summary: [], - topic: { header: '', bullets: [] }, - actions: [], - followUps: [], - }; this.isSessionActive = false; this.hasCompletedRecording = false; - this.sttMessages = []; this.viewMode = 'insights'; this.isHovering = false; this.isAnimating = false; this.elapsedTime = '00:00'; this.captureStartTime = null; this.timerInterval = null; - this.resizeObserver = null; this.adjustHeightThrottle = null; this.isThrottled = false; - this._shouldScrollAfterUpdate = false; - this.messageIdCounter = 0; this.copyState = 'idle'; this.copyTimeout = null; - // λ§ˆν¬λ‹€μš΄ 라이브러리 μ΄ˆκΈ°ν™” - this.marked = null; - this.hljs = null; - this.isLibrariesLoaded = false; - this.DOMPurify = null; - this.isDOMPurifyLoaded = false; - - // --- Debug Utilities --- - this._debug = { - enabled: false, // Set to false to disable debug messages - interval: null, - counter: 1, - }; - this.handleSttUpdate = this.handleSttUpdate.bind(this); this.adjustWindowHeight = this.adjustWindowHeight.bind(this); - - this.loadLibraries(); } - // --- Debug Utilities --- - _startDebugStream() { - if (!this._debug.enabled) return; - - this._debug.interval = setInterval(() => { - const speaker = this._debug.counter % 2 === 0 ? 'You' : 'Other Person'; - const text = `이것은 ${this._debug.counter}번째 μžλ™ 생성 λ©”μ‹œμ§€μž…λ‹ˆλ‹€. UIκ°€ μžλ™μœΌλ‘œ μ‘°μ ˆλ˜λŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.`; - - this._debug.counter++; - - this.handleSttUpdate(null, { speaker, text, isFinal: true }); - }, 1000); - } - - _stopDebugStream() { - if (this._debug.interval) { - clearInterval(this._debug.interval); + connectedCallback() { + super.connectedCallback(); + // Only start timer if session is active + if (this.isSessionActive) { + this.startTimer(); } - } + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.on('session-state-changed', (event, { isActive }) => { + const wasActive = this.isSessionActive; + this.isSessionActive = isActive; - async loadLibraries() { - try { - if (!window.marked) { - await this.loadScript('../../assets/marked-4.3.0.min.js'); - } - - if (!window.hljs) { - await this.loadScript('../../assets/highlight-11.9.0.min.js'); - } - - if (!window.DOMPurify) { - await this.loadScript('../../assets/dompurify-3.0.7.min.js'); - } - - this.marked = window.marked; - this.hljs = window.hljs; - this.DOMPurify = window.DOMPurify; - - if (this.marked && this.hljs) { - this.marked.setOptions({ - highlight: (code, lang) => { - if (lang && this.hljs.getLanguage(lang)) { - try { - return this.hljs.highlight(code, { language: lang }).value; - } catch (err) { - console.warn('Highlight error:', err); - } - } - try { - return this.hljs.highlightAuto(code).value; - } catch (err) { - console.warn('Auto highlight error:', err); - } - return code; - }, - breaks: true, - gfm: true, - pedantic: false, - smartypants: false, - xhtml: false, - }); - - this.isLibrariesLoaded = true; - console.log('Markdown libraries loaded successfully'); - } - - if (this.DOMPurify) { - this.isDOMPurifyLoaded = true; - console.log('DOMPurify loaded successfully in AssistantView'); - } - } catch (error) { - console.error('Failed to load libraries:', error); - } - } - - loadScript(src) { - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.src = src; - script.onload = resolve; - script.onerror = reject; - document.head.appendChild(script); - }); - } - - parseMarkdown(text) { - if (!text) return ''; - - if (!this.isLibrariesLoaded || !this.marked) { - return text; - } - - try { - return this.marked(text); - } catch (error) { - console.error('Markdown parsing error:', error); - return text; - } - } - - handleMarkdownClick(originalText) { - this.handleRequestClick(originalText); - } - - renderMarkdownContent() { - if (!this.isLibrariesLoaded || !this.marked) { - return; - } - - const markdownElements = this.shadowRoot.querySelectorAll('[data-markdown-id]'); - markdownElements.forEach(element => { - const originalText = element.getAttribute('data-original-text'); - if (originalText) { - try { - let parsedHTML = this.parseMarkdown(originalText); - - if (this.isDOMPurifyLoaded && this.DOMPurify) { - parsedHTML = this.DOMPurify.sanitize(parsedHTML); - - if (this.DOMPurify.removed && this.DOMPurify.removed.length > 0) { - console.warn('Unsafe content detected in insights, showing plain text'); - element.textContent = '⚠️ ' + originalText; - return; - } - } - - element.innerHTML = parsedHTML; - } catch (error) { - console.error('Error rendering markdown for element:', error); - element.textContent = originalText; + if (!wasActive && isActive) { + this.hasCompletedRecording = false; + this.startTimer(); + // Reset child components + this.updateComplete.then(() => { + const sttView = this.shadowRoot.querySelector('stt-view'); + const summaryView = this.shadowRoot.querySelector('summary-view'); + if (sttView) sttView.resetTranscript(); + if (summaryView) summaryView.resetAnalysis(); + }); + this.requestUpdate(); } - } - }); + if (wasActive && !isActive) { + this.hasCompletedRecording = true; + this.stopTimer(); + this.requestUpdate(); + } + }); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.stopTimer(); + + if (this.adjustHeightThrottle) { + clearTimeout(this.adjustHeightThrottle); + this.adjustHeightThrottle = null; + } + if (this.copyTimeout) { + clearTimeout(this.copyTimeout); + } } startTimer() { @@ -772,10 +580,9 @@ export class AssistantView extends LitElement { this.updateComplete .then(() => { const topBar = this.shadowRoot.querySelector('.top-bar'); - const activeContent = - this.viewMode === 'transcript' - ? this.shadowRoot.querySelector('.transcription-container') - : this.shadowRoot.querySelector('.insights-container'); + const activeContent = this.viewMode === 'transcript' + ? this.shadowRoot.querySelector('stt-view') + : this.shadowRoot.querySelector('summary-view'); if (!topBar || !activeContent) return; @@ -783,9 +590,9 @@ export class AssistantView extends LitElement { const contentHeight = activeContent.scrollHeight; - const idealHeight = topBarHeight + contentHeight + 20; + const idealHeight = topBarHeight + contentHeight; - const targetHeight = Math.min(700, Math.max(200, idealHeight)); + const targetHeight = Math.min(700, idealHeight); console.log( `[Height Adjusted] Mode: ${this.viewMode}, TopBar: ${topBarHeight}px, Content: ${contentHeight}px, Ideal: ${idealHeight}px, Target: ${targetHeight}px` @@ -814,62 +621,17 @@ export class AssistantView extends LitElement { this.requestUpdate(); } - parseOutlineData() { - const result = { - currentSummary: [], - mainTopicHeading: '', - mainTopicBullets: [], - }; - - if (!this.outlines || this.outlines.length === 0) { - return result; - } - - const allBullets = this.outlines.filter(item => item.startsWith('BULLET::')); - if (allBullets.length > 0) { - result.currentSummary.push(allBullets[0].replace('BULLET::', '').trim()); - } - - const heading = this.outlines.find(item => item.startsWith('HEADING::')); - if (heading) { - result.mainTopicHeading = heading.replace('HEADING::', '').trim(); - } - - if (allBullets.length > 1) { - result.mainTopicBullets = allBullets.slice(1).map(item => item.replace('BULLET::', '').trim()); - } - - return result; - } - async handleCopy() { if (this.copyState === 'copied') return; let textToCopy = ''; if (this.viewMode === 'transcript') { - textToCopy = this.sttMessages.map(msg => `${msg.speaker}: ${msg.text}`).join('\n'); + const sttView = this.shadowRoot.querySelector('stt-view'); + textToCopy = sttView ? sttView.getTranscriptText() : ''; } else { - const data = this.structuredData || { summary: [], topic: { header: '', bullets: [] }, actions: [] }; - let sections = []; - - if (data.summary && data.summary.length > 0) { - sections.push(`Current Summary:\n${data.summary.map(s => `β€’ ${s}`).join('\n')}`); - } - - if (data.topic && data.topic.header && data.topic.bullets.length > 0) { - sections.push(`\n${data.topic.header}:\n${data.topic.bullets.map(b => `β€’ ${b}`).join('\n')}`); - } - - if (data.actions && data.actions.length > 0) { - sections.push(`\nActions:\n${data.actions.map(a => `β–Έ ${a}`).join('\n')}`); - } - - if (data.followUps && data.followUps.length > 0) { - sections.push(`\nFollow-Ups:\n${data.followUps.map(f => `β–Έ ${f}`).join('\n')}`); - } - - textToCopy = sections.join('\n\n').trim(); + const summaryView = this.shadowRoot.querySelector('summary-view'); + textToCopy = summaryView ? summaryView.getSummaryText() : ''; } try { @@ -906,177 +668,24 @@ export class AssistantView extends LitElement { }, 16); } - handleSttUpdate(event, { speaker, text, isFinal, isPartial }) { - if (text === undefined) return; + updated(changedProperties) { + super.updated(changedProperties); - const container = this.shadowRoot.querySelector('.transcription-container'); - this._shouldScrollAfterUpdate = container ? container.scrollTop + container.clientHeight >= container.scrollHeight - 10 : false; - - const findLastPartialIdx = spk => { - for (let i = this.sttMessages.length - 1; i >= 0; i--) { - const m = this.sttMessages[i]; - if (m.speaker === spk && m.isPartial) return i; - } - return -1; - }; - - const newMessages = [...this.sttMessages]; - const targetIdx = findLastPartialIdx(speaker); - - if (isPartial) { - if (targetIdx !== -1) { - newMessages[targetIdx] = { - ...newMessages[targetIdx], - text, - isPartial: true, - isFinal: false, - }; - } else { - newMessages.push({ - id: this.messageIdCounter++, - speaker, - text, - isPartial: true, - isFinal: false, - }); - } - } else if (isFinal) { - if (targetIdx !== -1) { - newMessages[targetIdx] = { - ...newMessages[targetIdx], - text, - isPartial: false, - isFinal: true, - }; - } else { - newMessages.push({ - id: this.messageIdCounter++, - speaker, - text, - isPartial: false, - isFinal: true, - }); - } - } - - this.sttMessages = newMessages; - } - - scrollToTranscriptionBottom() { - setTimeout(() => { - const container = this.shadowRoot.querySelector('.transcription-container'); - if (container) { - container.scrollTop = container.scrollHeight; - } - }, 0); - } - - async handleRequestClick(requestText) { - console.log('πŸ”₯ Analysis request clicked:', requestText); - - if (window.require) { - const { ipcRenderer } = window.require('electron'); - - try { - const isAskViewVisible = await ipcRenderer.invoke('is-window-visible', 'ask'); - - if (!isAskViewVisible) { - await ipcRenderer.invoke('toggle-feature', 'ask'); - await new Promise(resolve => setTimeout(resolve, 100)); - } - - const result = await ipcRenderer.invoke('send-question-to-ask', requestText); - - if (result.success) { - console.log('βœ… Question sent to AskView successfully'); - } else { - console.error('❌ Failed to send question to AskView:', result.error); - } - } catch (error) { - console.error('❌ Error in handleRequestClick:', error); - } + if (changedProperties.has('viewMode')) { + this.adjustWindowHeight(); } } - connectedCallback() { - super.connectedCallback(); - this.startTimer(); - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.on('stt-update', this.handleSttUpdate); - ipcRenderer.on('session-state-changed', (event, { isActive }) => { - const wasActive = this.isSessionActive; - this.isSessionActive = isActive; - - if (!wasActive && isActive) { - this.hasCompletedRecording = false; - - // πŸ”„ Reset transcript & analysis when a fresh session starts - this.sttMessages = []; - this.structuredData = { - summary: [], - topic: { header: '', bullets: [] }, - actions: [], - followUps: [], - }; - this.requestUpdate(); - } - if (wasActive && !isActive) { - this.hasCompletedRecording = true; - - this.requestUpdate(); - } - }); - } - this._startDebugStream(); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.stopTimer(); - - if (this.adjustHeightThrottle) { - clearTimeout(this.adjustHeightThrottle); - this.adjustHeightThrottle = null; - } - if (this.copyTimeout) { - clearTimeout(this.copyTimeout); - } - - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.removeListener('stt-update', this.handleSttUpdate); - } - - this._stopDebugStream(); + handleSttMessagesUpdated(event) { + // Handle messages update from SttView if needed + this.adjustWindowHeightThrottled(); } firstUpdated() { super.firstUpdated(); - setTimeout(() => this.adjustWindowHeight(), 200); } - updated(changedProperties) { - super.updated(changedProperties); - - this.renderMarkdownContent(); - - if (changedProperties.has('sttMessages')) { - if (this._shouldScrollAfterUpdate) { - this.scrollToTranscriptionBottom(); - this._shouldScrollAfterUpdate = false; - } - this.adjustWindowHeightThrottled(); - } - - if (changedProperties.has('viewMode')) { - this.adjustWindowHeight(); - } else if (changedProperties.has('outlines') || changedProperties.has('analysisRequests') || changedProperties.has('structuredData')) { - this.adjustWindowHeightThrottled(); - } - } - render() { const displayText = this.isHovering ? this.viewMode === 'transcript' @@ -1086,16 +695,6 @@ export class AssistantView extends LitElement { ? `Live insights` : `Glass is Listening ${this.elapsedTime}`; - const data = this.structuredData || { - summary: [], - topic: { header: '', bullets: [] }, - actions: [], - }; - - const getSpeakerClass = speaker => { - return speaker.toLowerCase() === 'me' ? 'me' : 'them'; - }; - return html`
@@ -1137,84 +736,15 @@ export class AssistantView extends LitElement {
-
- ${this.sttMessages.map(msg => html`
${msg.text}
`)} -
+ -
- Current Summary - ${data.summary.length > 0 - ? data.summary - .slice(0, 5) - .map( - (bullet, index) => html` -
this.handleMarkdownClick(bullet)} - > - ${bullet} -
- ` - ) - : html`
No content yet...
`} - ${data.topic.header - ? html` - ${data.topic.header} - ${data.topic.bullets - .slice(0, 3) - .map( - (bullet, index) => html` -
this.handleMarkdownClick(bullet)} - > - ${bullet} -
- ` - )} - ` - : ''} - ${data.actions.length > 0 - ? html` - Actions - ${data.actions - .slice(0, 5) - .map( - (action, index) => html` -
this.handleMarkdownClick(action)} - > - ${action} -
- ` - )} - ` - : ''} - ${this.hasCompletedRecording && data.followUps && data.followUps.length > 0 - ? html` - Follow-Ups - ${data.followUps.map( - (followUp, index) => html` -
this.handleMarkdownClick(followUp)} - > - ${followUp} -
- ` - )} - ` - : ''} -
+ `; } diff --git a/src/features/listen/SetupView.js b/src/features/listen/SetupView.js deleted file mode 100644 index 88909d9..0000000 --- a/src/features/listen/SetupView.js +++ /dev/null @@ -1,179 +0,0 @@ -import { html, css, LitElement } from '../../assets/lit-core-2.7.4.min.js'; - -export class SetupView extends LitElement { - static styles = css` - * { - font-family: 'Helvetica Neue', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - cursor: default; - user-select: none; - } - - .welcome { - color: white; - font-size: 12px; - font-family: 'Helvetica Neue', sans-serif; - font-weight: 500; - margin-bottom: 8px; - margin-top: auto; - } - - .input-group { - display: flex; - gap: 12px; - margin-bottom: 20px; - } - - .input-group input { - flex: 1; - } - - input { - background: rgba(255, 255, 255, 0.20); - border-radius: 8px; - outline: 1px rgba(255, 255, 255, 0.50) solid; - outline-offset: -1px; - backdrop-filter: blur(0.50px); - border: none; - color: white; - padding: 12px 16px; - width: 100%; - font-size: 14px; - font-family: 'Helvetica Neue', sans-serif; - font-weight: 400; - } - - input:focus { - outline: none; - } - - input::placeholder { - color: rgba(255, 255, 255, 0.5); - } - - .start-button { - background: var(--start-button-background); - color: var(--start-button-color); - border: 1px solid var(--start-button-border); - padding: 8px 16px; - border-radius: 8px; - font-size: 13px; - font-weight: 500; - } - - .start-button:hover { - background: var(--start-button-hover-background); - border-color: var(--start-button-hover-border); - } - - .start-button.initializing { - opacity: 0.5; - } - - .start-button.initializing:hover { - background: var(--start-button-background); - border-color: var(--start-button-border); - } - - .description { - color: var(--description-color); - font-size: 14px; - margin-bottom: 24px; - line-height: 1.5; - } - - .link { - color: var(--link-color); - text-decoration: underline; - cursor: pointer; - } - - :host { - height: 100%; - display: flex; - flex-direction: column; - width: 100%; - max-width: 500px; - } - `; - - static properties = { - onStart: { type: Function }, - onAPIKeyHelp: { type: Function }, - isInitializing: { type: Boolean }, - onLayoutModeChange: { type: Function }, - }; - - constructor() { - super(); - this.onStart = () => {}; - this.onAPIKeyHelp = () => {}; - this.isInitializing = false; - this.onLayoutModeChange = () => {}; - } - - connectedCallback() { - super.connectedCallback(); - window.electron?.ipcRenderer?.on('session-initializing', (event, isInitializing) => { - this.isInitializing = isInitializing; - }); - - // Load and apply layout mode on startup - this.loadLayoutMode(); - } - - disconnectedCallback() { - super.disconnectedCallback(); - window.electron?.ipcRenderer?.removeAllListeners('session-initializing'); - } - - handleInput(e) { - localStorage.setItem('apiKey', e.target.value); - } - - handleStartClick() { - if (this.isInitializing) { - return; - } - this.onStart(); - } - - handleAPIKeyHelpClick() { - this.onAPIKeyHelp(); - } - - handleResetOnboarding() { - localStorage.removeItem('onboardingCompleted'); - // Refresh the page to trigger onboarding - window.location.reload(); - } - - loadLayoutMode() { - const savedLayoutMode = localStorage.getItem('layoutMode'); - if (savedLayoutMode && savedLayoutMode !== 'normal') { - // Notify parent component to apply the saved layout mode - this.onLayoutModeChange(savedLayoutMode); - } - } - - render() { - return html` -
Welcome
- -
- - -
-

- dont have an api key? - get one here -

- `; - } -} - -customElements.define('setup-view', SetupView); diff --git a/src/features/listen/audioUtils.js b/src/features/listen/audioUtils.js deleted file mode 100644 index 25dfb9d..0000000 --- a/src/features/listen/audioUtils.js +++ /dev/null @@ -1,123 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -function pcmToWav(pcmBuffer, outputPath, sampleRate = 24000, channels = 1, bitDepth = 16) { - const byteRate = sampleRate * channels * (bitDepth / 8); - const blockAlign = channels * (bitDepth / 8); - const dataSize = pcmBuffer.length; - - const header = Buffer.alloc(44); - - header.write('RIFF', 0); - header.writeUInt32LE(dataSize + 36, 4); - header.write('WAVE', 8); - - header.write('fmt ', 12); - header.writeUInt32LE(16, 16); - header.writeUInt16LE(1, 20); - header.writeUInt16LE(channels, 22); - header.writeUInt32LE(sampleRate, 24); - header.writeUInt32LE(byteRate, 28); - header.writeUInt16LE(blockAlign, 32); - header.writeUInt16LE(bitDepth, 34); - - header.write('data', 36); - header.writeUInt32LE(dataSize, 40); - - const wavBuffer = Buffer.concat([header, pcmBuffer]); - - fs.writeFileSync(outputPath, wavBuffer); - - return outputPath; -} - -function analyzeAudioBuffer(buffer, label = 'Audio') { - const int16Array = new Int16Array(buffer.buffer, buffer.byteOffset, buffer.length / 2); - - let minValue = 32767; - let maxValue = -32768; - let avgValue = 0; - let rmsValue = 0; - let silentSamples = 0; - - for (let i = 0; i < int16Array.length; i++) { - const sample = int16Array[i]; - minValue = Math.min(minValue, sample); - maxValue = Math.max(maxValue, sample); - avgValue += sample; - rmsValue += sample * sample; - - if (Math.abs(sample) < 100) { - silentSamples++; - } - } - - avgValue /= int16Array.length; - rmsValue = Math.sqrt(rmsValue / int16Array.length); - - const silencePercentage = (silentSamples / int16Array.length) * 100; - - console.log(`${label} Analysis:`); - console.log(` Samples: ${int16Array.length}`); - console.log(` Min: ${minValue}, Max: ${maxValue}`); - console.log(` Average: ${avgValue.toFixed(2)}`); - console.log(` RMS: ${rmsValue.toFixed(2)}`); - console.log(` Silence: ${silencePercentage.toFixed(1)}%`); - console.log(` Dynamic Range: ${20 * Math.log10(maxValue / (rmsValue || 1))} dB`); - - return { - minValue, - maxValue, - avgValue, - rmsValue, - silencePercentage, - sampleCount: int16Array.length, - }; -} - -function saveDebugAudio(buffer, type, timestamp = Date.now()) { - const homeDir = require('os').homedir(); - const debugDir = path.join(homeDir, '.pickle-glass', 'debug'); - - if (!fs.existsSync(debugDir)) { - fs.mkdirSync(debugDir, { recursive: true }); - } - - const pcmPath = path.join(debugDir, `${type}_${timestamp}.pcm`); - const wavPath = path.join(debugDir, `${type}_${timestamp}.wav`); - const metaPath = path.join(debugDir, `${type}_${timestamp}.json`); - - fs.writeFileSync(pcmPath, buffer); - - pcmToWav(buffer, wavPath); - - const analysis = analyzeAudioBuffer(buffer, type); - fs.writeFileSync( - metaPath, - JSON.stringify( - { - timestamp, - type, - bufferSize: buffer.length, - analysis, - format: { - sampleRate: 24000, - channels: 1, - bitDepth: 16, - }, - }, - null, - 2 - ) - ); - - console.log(`Debug audio saved: ${wavPath}`); - - return { pcmPath, wavPath, metaPath }; -} - -module.exports = { - pcmToWav, - analyzeAudioBuffer, - saveDebugAudio, -}; diff --git a/src/features/listen/listenService.js b/src/features/listen/listenService.js new file mode 100644 index 0000000..4218745 --- /dev/null +++ b/src/features/listen/listenService.js @@ -0,0 +1,300 @@ +const { BrowserWindow, app } = require('electron'); +const SttService = require('./stt/sttService'); +const SummaryService = require('./summary/summaryService'); +const authService = require('../../common/services/authService'); +const sessionRepository = require('../../common/repositories/session'); +const sttRepository = require('./stt/repositories'); + +class ListenService { + constructor() { + this.sttService = new SttService(); + this.summaryService = new SummaryService(); + this.currentSessionId = null; + this.isInitializingSession = false; + + this.setupServiceCallbacks(); + } + + setupServiceCallbacks() { + // STT service callbacks + this.sttService.setCallbacks({ + onTranscriptionComplete: (speaker, text) => { + this.handleTranscriptionComplete(speaker, text); + }, + onStatusUpdate: (status) => { + this.sendToRenderer('update-status', status); + } + }); + + // Summary service callbacks + this.summaryService.setCallbacks({ + onAnalysisComplete: (data) => { + console.log('πŸ“Š Analysis completed:', data); + }, + onStatusUpdate: (status) => { + this.sendToRenderer('update-status', status); + } + }); + } + + sendToRenderer(channel, data) { + BrowserWindow.getAllWindows().forEach(win => { + if (!win.isDestroyed()) { + win.webContents.send(channel, data); + } + }); + } + + async handleTranscriptionComplete(speaker, text) { + console.log(`[ListenService] Transcription complete: ${speaker} - ${text}`); + + // Save to database + await this.saveConversationTurn(speaker, text); + + // Add to summary service for analysis + this.summaryService.addConversationTurn(speaker, text); + } + + async saveConversationTurn(speaker, transcription) { + if (!this.currentSessionId) { + console.error('[DB] Cannot save turn, no active session ID.'); + return; + } + if (transcription.trim() === '') return; + + try { + await sessionRepository.touch(this.currentSessionId); + await sttRepository.addTranscript({ + sessionId: this.currentSessionId, + speaker: speaker, + text: transcription.trim(), + }); + console.log(`[DB] Saved transcript for session ${this.currentSessionId}: (${speaker})`); + } catch (error) { + console.error('Failed to save transcript to DB:', error); + } + } + + async initializeNewSession() { + try { + const uid = authService.getCurrentUserId(); + if (!uid) { + throw new Error("Cannot initialize session: user not logged in."); + } + + this.currentSessionId = await sessionRepository.getOrCreateActive(uid, 'listen'); + console.log(`[DB] New listen session ensured: ${this.currentSessionId}`); + + // Set session ID for summary service + this.summaryService.setSessionId(this.currentSessionId); + + // Reset conversation history + this.summaryService.resetConversationHistory(); + + console.log('New conversation session started:', this.currentSessionId); + return true; + } catch (error) { + console.error('Failed to initialize new session in DB:', error); + this.currentSessionId = null; + return false; + } + } + + async initializeSession(language = 'en') { + if (this.isInitializingSession) { + console.log('Session initialization already in progress.'); + return false; + } + + this.isInitializingSession = true; + this.sendToRenderer('session-initializing', true); + this.sendToRenderer('update-status', 'Initializing sessions...'); + + try { + // Initialize database session + const sessionInitialized = await this.initializeNewSession(); + if (!sessionInitialized) { + throw new Error('Failed to initialize database session'); + } + + /* ---------- STT Initialization Retry Logic ---------- */ + const MAX_RETRY = 10; + const RETRY_DELAY_MS = 300; // 0.3 seconds + + let sttReady = false; + for (let attempt = 1; attempt <= MAX_RETRY; attempt++) { + try { + await this.sttService.initializeSttSessions(language); + sttReady = true; + break; // Exit on success + } catch (err) { + console.warn( + `[ListenService] STT init attempt ${attempt} failed: ${err.message}` + ); + if (attempt < MAX_RETRY) { + await new Promise(r => setTimeout(r, RETRY_DELAY_MS)); + } + } + } + if (!sttReady) throw new Error('STT init failed after retries'); + /* ------------------------------------------- */ + + console.log('βœ… Listen service initialized successfully.'); + + this.sendToRenderer('session-state-changed', { isActive: true }); + this.sendToRenderer('update-status', 'Connected. Ready to listen.'); + + return true; + } catch (error) { + console.error('❌ Failed to initialize listen service:', error); + this.sendToRenderer('update-status', 'Initialization failed.'); + return false; + } finally { + this.isInitializingSession = false; + this.sendToRenderer('session-initializing', false); + } + } + + async sendAudioContent(data, mimeType) { + return await this.sttService.sendAudioContent(data, mimeType); + } + + async startMacOSAudioCapture() { + if (process.platform !== 'darwin') { + throw new Error('macOS audio capture only available on macOS'); + } + return await this.sttService.startMacOSAudioCapture(); + } + + async stopMacOSAudioCapture() { + this.sttService.stopMacOSAudioCapture(); + } + + isSessionActive() { + return this.sttService.isSessionActive(); + } + + async closeSession() { + try { + // Close STT sessions + await this.sttService.closeSessions(); + + // End database session + if (this.currentSessionId) { + await sessionRepository.end(this.currentSessionId); + console.log(`[DB] Session ${this.currentSessionId} ended.`); + } + + // Reset state + this.currentSessionId = null; + this.summaryService.resetConversationHistory(); + + this.sendToRenderer('session-state-changed', { isActive: false }); + this.sendToRenderer('session-did-close'); + + console.log('Listen service session closed.'); + return { success: true }; + } catch (error) { + console.error('Error closing listen service session:', error); + return { success: false, error: error.message }; + } + } + + getCurrentSessionData() { + return { + sessionId: this.currentSessionId, + conversationHistory: this.summaryService.getConversationHistory(), + totalTexts: this.summaryService.getConversationHistory().length, + analysisData: this.summaryService.getCurrentAnalysisData(), + }; + } + + getConversationHistory() { + return this.summaryService.getConversationHistory(); + } + + setupIpcHandlers() { + const { ipcMain } = require('electron'); + + ipcMain.handle('is-session-active', async () => { + const isActive = this.isSessionActive(); + console.log(`Checking session status. Active: ${isActive}`); + return isActive; + }); + + ipcMain.handle('initialize-openai', async (event, profile = 'interview', language = 'en') => { + console.log(`Received initialize-openai request with profile: ${profile}, language: ${language}`); + const success = await this.initializeSession(language); + return success; + }); + + ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => { + try { + await this.sendAudioContent(data, mimeType); + return { success: true }; + } catch (e) { + console.error('Error sending user audio:', e); + return { success: false, error: e.message }; + } + }); + + ipcMain.handle('send-system-audio-content', async (event, { data, mimeType }) => { + try { + await this.sttService.sendSystemAudioContent(data, mimeType); + + // Send system audio data back to renderer for AEC reference (like macOS does) + this.sendToRenderer('system-audio-data', { data }); + + return { success: true }; + } catch (error) { + console.error('Error sending system audio:', error); + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('start-macos-audio', async () => { + if (process.platform !== 'darwin') { + return { success: false, error: 'macOS audio capture only available on macOS' }; + } + if (this.sttService.isMacOSAudioRunning?.()) { + return { success: false, error: 'already_running' }; + } + + try { + const success = await this.startMacOSAudioCapture(); + return { success, error: null }; + } catch (error) { + console.error('Error starting macOS audio capture:', error); + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('stop-macos-audio', async () => { + try { + this.stopMacOSAudioCapture(); + return { success: true }; + } catch (error) { + console.error('Error stopping macOS audio capture:', error); + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('close-session', async () => { + return await this.closeSession(); + }); + + ipcMain.handle('update-google-search-setting', async (event, enabled) => { + try { + console.log('Google Search setting updated to:', enabled); + return { success: true }; + } catch (error) { + console.error('Error updating Google Search setting:', error); + return { success: false, error: error.message }; + } + }); + + console.log('βœ… Listen service IPC handlers registered'); + } +} + +module.exports = ListenService; \ No newline at end of file diff --git a/src/features/listen/liveSummaryService.js b/src/features/listen/liveSummaryService.js deleted file mode 100644 index e786ba2..0000000 --- a/src/features/listen/liveSummaryService.js +++ /dev/null @@ -1,1058 +0,0 @@ -require('dotenv').config(); -const { BrowserWindow, ipcMain } = require('electron'); -const { spawn } = require('child_process'); -const { saveDebugAudio } = require('./audioUtils.js'); -const { getSystemPrompt } = require('../../common/prompts/promptBuilder.js'); -const { connectToGeminiSession } = require('../../common/services/googleGeminiClient.js'); -const { connectToOpenAiSession, createOpenAiGenerativeClient, getOpenAiGenerativeModel } = require('../../common/services/openAiClient.js'); -const { makeChatCompletionWithPortkey } = require('../../common/services/aiProviderService.js'); -const sqliteClient = require('../../common/services/sqliteClient'); -const dataService = require('../../common/services/dataService'); - -const { isFirebaseLoggedIn, getCurrentFirebaseUser, getStoredProvider } = require('../../electron/windowManager.js'); - -function getApiKey() { - const { getStoredApiKey } = require('../../electron/windowManager.js'); - const storedKey = getStoredApiKey(); - - if (storedKey) { - console.log('[LiveSummaryService] Using stored API key'); - return storedKey; - } - - const envKey = process.env.OPENAI_API_KEY; - if (envKey) { - console.log('[LiveSummaryService] Using environment API key'); - return envKey; - } - - console.error('[LiveSummaryService] No API key found in storage or environment'); - return null; -} - -async function getAiProvider() { - try { - const { ipcRenderer } = require('electron'); - const provider = await ipcRenderer.invoke('get-ai-provider'); - return provider || 'openai'; - } catch (error) { - // If we're in the main process, get it directly - const { getStoredProvider } = require('../../electron/windowManager.js'); - return getStoredProvider ? getStoredProvider() : 'openai'; - } -} - -let currentSessionId = null; -let conversationHistory = []; -let isInitializingSession = false; - -let mySttSession = null; -let theirSttSession = null; -let myCurrentUtterance = ''; -let theirCurrentUtterance = ''; - -let myLastPartialText = ''; -let theirLastPartialText = ''; -let myInactivityTimer = null; -let theirInactivityTimer = null; -const INACTIVITY_TIMEOUT = 3000; - -const SESSION_IDLE_TIMEOUT_SECONDS = 30 * 60; // 30 minutes - -let previousAnalysisResult = null; -let analysisHistory = []; - -// --------------------------------------------------------------------------- -// πŸŽ›οΈ Turn-completion debouncing -// --------------------------------------------------------------------------- -// Very aggressive VAD (e.g. 50 ms) tends to split one spoken sentence into -// many "completed" events. To avoid creating a separate chat bubble for each -// of those micro-turns we debounce the *completed* events per speaker. Any -// completions that arrive within this window are concatenated and flushed as -// **one** final turn. - -const COMPLETION_DEBOUNCE_MS = 2000; - -let myCompletionBuffer = ''; -let theirCompletionBuffer = ''; -let myCompletionTimer = null; -let theirCompletionTimer = null; - -function flushMyCompletion() { - if (!myCompletionBuffer.trim()) return; - - const finalText = myCompletionBuffer.trim(); - // Save to DB & send to renderer as final - saveConversationTurn('Me', finalText); - sendToRenderer('stt-update', { - speaker: 'Me', - text: finalText, - isPartial: false, - isFinal: true, - timestamp: Date.now(), - }); - - myCompletionBuffer = ''; - myCompletionTimer = null; - myCurrentUtterance = ''; // Reset utterance accumulator on flush - sendToRenderer('update-status', 'Listening...'); -} - -function flushTheirCompletion() { - if (!theirCompletionBuffer.trim()) return; - - const finalText = theirCompletionBuffer.trim(); - saveConversationTurn('Them', finalText); - sendToRenderer('stt-update', { - speaker: 'Them', - text: finalText, - isPartial: false, - isFinal: true, - timestamp: Date.now(), - }); - - theirCompletionBuffer = ''; - theirCompletionTimer = null; - theirCurrentUtterance = ''; // Reset utterance accumulator on flush - sendToRenderer('update-status', 'Listening...'); -} - -function debounceMyCompletion(text) { - // μƒλŒ€λ°©μ΄ λ§ν•˜κ³  있던 경우, ν™”μžκ°€ λ³€κ²½λ˜μ—ˆμœΌλ―€λ‘œ μ¦‰μ‹œ μƒλŒ€λ°©μ˜ 말풍선을 μ™„μ„±ν•©λ‹ˆλ‹€. - if (theirCompletionTimer) { - clearTimeout(theirCompletionTimer); - flushTheirCompletion(); - } - - myCompletionBuffer += (myCompletionBuffer ? ' ' : '') + text; - - if (myCompletionTimer) clearTimeout(myCompletionTimer); - myCompletionTimer = setTimeout(flushMyCompletion, COMPLETION_DEBOUNCE_MS); -} - -function debounceTheirCompletion(text) { - // λ‚΄κ°€ λ§ν•˜κ³  있던 경우, ν™”μžκ°€ λ³€κ²½λ˜μ—ˆμœΌλ―€λ‘œ μ¦‰μ‹œ λ‚΄ 말풍선을 μ™„μ„±ν•©λ‹ˆλ‹€. - if (myCompletionTimer) { - clearTimeout(myCompletionTimer); - flushMyCompletion(); - } - - theirCompletionBuffer += (theirCompletionBuffer ? ' ' : '') + text; - - if (theirCompletionTimer) clearTimeout(theirCompletionTimer); - theirCompletionTimer = setTimeout(flushTheirCompletion, COMPLETION_DEBOUNCE_MS); -} - -let systemAudioProc = null; - -let analysisIntervalId = null; - -/** - * Converts conversation history into text to include in the prompt. - * @param {Array} conversationTexts - Array of conversation texts ["me: ~~~", "them: ~~~", ...] - * @param {number} maxTurns - Maximum number of recent turns to include - * @returns {string} - Formatted conversation string for the prompt - */ -function formatConversationForPrompt(conversationTexts, maxTurns = 30) { - if (conversationTexts.length === 0) return ''; - return conversationTexts.slice(-maxTurns).join('\n'); -} - -async function makeOutlineAndRequests(conversationTexts, maxTurns = 30) { - console.log(`πŸ” makeOutlineAndRequests called - conversationTexts: ${conversationTexts.length}`); - - if (conversationTexts.length === 0) { - console.log('⚠️ No conversation texts available for analysis'); - return null; - } - - const recentConversation = formatConversationForPrompt(conversationTexts, maxTurns); - - // 이전 뢄석 κ²°κ³Όλ₯Ό ν”„λ‘¬ν”„νŠΈμ— 포함 - let contextualPrompt = ''; - if (previousAnalysisResult) { - contextualPrompt = ` -Previous Analysis Context: -- Main Topic: ${previousAnalysisResult.topic.header} -- Key Points: ${previousAnalysisResult.summary.slice(0, 3).join(', ')} -- Last Actions: ${previousAnalysisResult.actions.slice(0, 2).join(', ')} - -Please build upon this context while analyzing the new conversation segments. -`; - } - - const basePrompt = getSystemPrompt('pickle_glass_analysis', '', false); - const systemPrompt = basePrompt.replace('{{CONVERSATION_HISTORY}}', recentConversation); - - try { - const messages = [ - { - role: 'system', - content: systemPrompt, - }, - { - role: 'user', - content: `${contextualPrompt} - -Analyze the conversation and provide a structured summary. Format your response as follows: - -**Summary Overview** -- Main discussion point with context - -**Key Topic: [Topic Name]** -- First key insight -- Second key insight -- Third key insight - -**Extended Explanation** -Provide 2-3 sentences explaining the context and implications. - -**Suggested Questions** -1. First follow-up question? -2. Second follow-up question? -3. Third follow-up question? - -Keep all points concise and build upon previous analysis if provided.`, - }, - ]; - - console.log('πŸ€– Sending analysis request to OpenAI...'); - - const API_KEY = getApiKey(); - if (!API_KEY) { - throw new Error('No API key available'); - } - - const provider = getStoredProvider ? getStoredProvider() : 'openai'; - const loggedIn = isFirebaseLoggedIn(); // true ➜ vKey, false ➜ apiKey - const usePortkey = loggedIn && provider === 'openai'; // Only use Portkey for OpenAI with Firebase - - console.log(`[LiveSummary] provider: ${provider}, usePortkey: ${usePortkey}`); - - const completion = await makeChatCompletionWithPortkey({ - apiKey: API_KEY, - provider: provider, - messages: messages, - temperature: 0.7, - maxTokens: 1024, - model: provider === 'openai' ? 'gpt-4.1' : 'gemini-2.5-flash', - usePortkey: usePortkey, - portkeyVirtualKey: usePortkey ? API_KEY : null - }); - - const responseText = completion.content; - console.log(`βœ… Analysis response received: ${responseText}`); - const structuredData = parseResponseText(responseText, previousAnalysisResult); - - if (currentSessionId) { - sqliteClient.saveSummary({ - sessionId: currentSessionId, - tldr: structuredData.summary.join('\n'), - bullet_json: JSON.stringify(structuredData.topic.bullets), - action_json: JSON.stringify(structuredData.actions), - model: 'gpt-4.1' - }).catch(err => console.error('[DB] Failed to save summary:', err)); - } - - // 뢄석 κ²°κ³Ό μ €μž₯ - previousAnalysisResult = structuredData; - analysisHistory.push({ - timestamp: Date.now(), - data: structuredData, - conversationLength: conversationTexts.length, - }); - - // νžˆμŠ€ν† λ¦¬ 크기 μ œν•œ (졜근 10개만 μœ μ§€) - if (analysisHistory.length > 10) { - analysisHistory.shift(); - } - - return structuredData; - } catch (error) { - console.error('❌ Error during analysis generation:', error.message); - return previousAnalysisResult; // μ—λŸ¬ μ‹œ 이전 κ²°κ³Ό λ°˜ν™˜ - } -} - -function parseResponseText(responseText, previousResult) { - const structuredData = { - summary: [], - topic: { header: '', bullets: [] }, - actions: [], - followUps: ['βœ‰οΈ Draft a follow-up email', 'βœ… Generate action items', 'πŸ“ Show summary'], - }; - - // 이전 κ²°κ³Όκ°€ 있으면 κΈ°λ³Έκ°’μœΌλ‘œ μ‚¬μš© - if (previousResult) { - structuredData.topic.header = previousResult.topic.header; - structuredData.summary = [...previousResult.summary]; - } - - try { - const lines = responseText.split('\n'); - let currentSection = ''; - let isCapturingTopic = false; - let topicName = ''; - - for (const line of lines) { - const trimmedLine = line.trim(); - - // μ„Ήμ…˜ 헀더 감지 - if (trimmedLine.startsWith('**Summary Overview**')) { - currentSection = 'summary-overview'; - continue; - } else if (trimmedLine.startsWith('**Key Topic:')) { - currentSection = 'topic'; - isCapturingTopic = true; - topicName = trimmedLine.match(/\*\*Key Topic: (.+?)\*\*/)?.[1] || ''; - if (topicName) { - structuredData.topic.header = topicName + ':'; - } - continue; - } else if (trimmedLine.startsWith('**Extended Explanation**')) { - currentSection = 'explanation'; - continue; - } else if (trimmedLine.startsWith('**Suggested Questions**')) { - currentSection = 'questions'; - continue; - } - - // 컨텐츠 νŒŒμ‹± - if (trimmedLine.startsWith('-') && currentSection === 'summary-overview') { - const summaryPoint = trimmedLine.substring(1).trim(); - if (summaryPoint && !structuredData.summary.includes(summaryPoint)) { - // κΈ°μ‘΄ summary μ—…λ°μ΄νŠΈ (μ΅œλŒ€ 5개 μœ μ§€) - structuredData.summary.unshift(summaryPoint); - if (structuredData.summary.length > 5) { - structuredData.summary.pop(); - } - } - } else if (trimmedLine.startsWith('-') && currentSection === 'topic') { - const bullet = trimmedLine.substring(1).trim(); - if (bullet && structuredData.topic.bullets.length < 3) { - structuredData.topic.bullets.push(bullet); - } - } else if (currentSection === 'explanation' && trimmedLine) { - // explanation을 topic bullets에 μΆ”κ°€ (λ¬Έμž₯ λ‹¨μœ„λ‘œ) - const sentences = trimmedLine - .split(/\.\s+/) - .filter(s => s.trim().length > 0) - .map(s => s.trim() + (s.endsWith('.') ? '' : '.')); - - sentences.forEach(sentence => { - if (structuredData.topic.bullets.length < 3 && !structuredData.topic.bullets.includes(sentence)) { - structuredData.topic.bullets.push(sentence); - } - }); - } else if (trimmedLine.match(/^\d+\./) && currentSection === 'questions') { - const question = trimmedLine.replace(/^\d+\.\s*/, '').trim(); - if (question && question.includes('?')) { - structuredData.actions.push(`❓ ${question}`); - } - } - } - - // κΈ°λ³Έ μ•‘μ…˜ μΆ”κ°€ - const defaultActions = ['✨ What should I say next?', 'πŸ’¬ Suggest follow-up questions']; - defaultActions.forEach(action => { - if (!structuredData.actions.includes(action)) { - structuredData.actions.push(action); - } - }); - - // μ•‘μ…˜ 개수 μ œν•œ - structuredData.actions = structuredData.actions.slice(0, 5); - - // μœ νš¨μ„± 검증 및 이전 데이터 병합 - if (structuredData.summary.length === 0 && previousResult) { - structuredData.summary = previousResult.summary; - } - if (structuredData.topic.bullets.length === 0 && previousResult) { - structuredData.topic.bullets = previousResult.topic.bullets; - } - } catch (error) { - console.error('❌ Error parsing response text:', error); - // μ—λŸ¬ μ‹œ 이전 κ²°κ³Ό λ°˜ν™˜ - return ( - previousResult || { - summary: [], - topic: { header: 'Analysis in progress', bullets: [] }, - actions: ['✨ What should I say next?', 'πŸ’¬ Suggest follow-up questions'], - followUps: ['βœ‰οΈ Draft a follow-up email', 'βœ… Generate action items', 'πŸ“ Show summary'], - } - ); - } - - console.log('πŸ“Š Final structured data:', JSON.stringify(structuredData, null, 2)); - return structuredData; -} - -/** - * Triggers analysis when conversation history reaches 5 texts. - */ -async function triggerAnalysisIfNeeded() { - if (conversationHistory.length >= 5 && conversationHistory.length % 5 === 0) { - console.log(`πŸš€ Triggering analysis (non-blocking) - ${conversationHistory.length} conversation texts accumulated`); - - makeOutlineAndRequests(conversationHistory) - .then(data => { - if (data) { - console.log('πŸ“€ Sending structured data to renderer'); - sendToRenderer('update-structured-data', data); - } else { - console.log('❌ No analysis data returned from non-blocking call'); - } - }) - .catch(error => { - console.error('❌ Error in non-blocking analysis:', error); - }); - } -} - -/** - * Schedules periodic updates of outline and analysis every 10 seconds. - DEPRECATED - * Now analysis is triggered every 5 conversation texts. - */ -function startAnalysisInterval() { - console.log('⏰ Analysis will be triggered every 5 conversation texts (not on timer)'); - - if (analysisIntervalId) { - clearInterval(analysisIntervalId); - analysisIntervalId = null; - } -} - -function stopAnalysisInterval() { - if (analysisIntervalId) { - clearInterval(analysisIntervalId); - analysisIntervalId = null; - } - - if (myInactivityTimer) { - clearTimeout(myInactivityTimer); - myInactivityTimer = null; - } - if (theirInactivityTimer) { - clearTimeout(theirInactivityTimer); - theirInactivityTimer = null; - } -} - -function sendToRenderer(channel, data) { - BrowserWindow.getAllWindows().forEach(win => { - if (!win.isDestroyed()) { - win.webContents.send(channel, data); - } - }); -} - -function getCurrentSessionData() { - return { - sessionId: currentSessionId, - conversationHistory: conversationHistory, - totalTexts: conversationHistory.length, - }; -} - -// Conversation management functions -async function getOrCreateActiveSession(requestedType = 'ask') { - // 1. Check for an existing, valid session - if (currentSessionId) { - const session = await sqliteClient.getSession(currentSessionId); - - if (session && !session.ended_at) { - // Ask sessions can expire, Listen sessions can't (they are closed explicitly) - const isExpired = session.session_type === 'ask' && - (Date.now() / 1000) - session.updated_at > SESSION_IDLE_TIMEOUT_SECONDS; - - if (!isExpired) { - // Session is valid, potentially promote it - if (requestedType === 'listen' && session.session_type === 'ask') { - await sqliteClient.updateSessionType(currentSessionId, 'listen'); - console.log(`[Session] Promoted session ${currentSessionId} to 'listen'.`); - } else { - await sqliteClient.touchSession(currentSessionId); - } - return currentSessionId; - } else { - console.log(`[Session] Ask session ${currentSessionId} expired. Closing it.`); - await sqliteClient.endSession(currentSessionId); - currentSessionId = null; // Important: clear the expired session ID - } - } - } - - // 2. If no valid session, create a new one - console.log(`[Session] No active session found. Creating a new one with type: ${requestedType}`); - const uid = dataService.currentUserId; - currentSessionId = await sqliteClient.createSession(uid, requestedType); - - // Clear old conversation data for the new session - conversationHistory = []; - myCurrentUtterance = ''; - theirCurrentUtterance = ''; - previousAnalysisResult = null; - analysisHistory = []; - - return currentSessionId; -} - -async function initializeNewSession() { - try { - currentSessionId = await getOrCreateActiveSession('listen'); - console.log(`[DB] New listen session ensured: ${currentSessionId}`); - - conversationHistory = []; - myCurrentUtterance = ''; - theirCurrentUtterance = ''; - - // πŸ”„ Reset analysis state so the new session starts fresh - previousAnalysisResult = null; - analysisHistory = []; - - // sendToRenderer('update-outline', []); - // sendToRenderer('update-analysis-requests', []); - - myLastPartialText = ''; - theirLastPartialText = ''; - if (myInactivityTimer) { - clearTimeout(myInactivityTimer); - myInactivityTimer = null; - } - if (theirInactivityTimer) { - clearTimeout(theirInactivityTimer); - theirInactivityTimer = null; - } - - console.log('New conversation session started:', currentSessionId); - return true; - } catch (error) { - console.error('Failed to initialize new session in DB:', error); - currentSessionId = null; - return false; - } -} - -async function saveConversationTurn(speaker, transcription) { - if (!currentSessionId) { - console.error('[DB] Cannot save turn, no active session ID.'); - return; - } - if (transcription.trim() === '') return; - - try { - await sqliteClient.addTranscript({ - sessionId: currentSessionId, - speaker: speaker, - text: transcription.trim(), - }); - console.log(`[DB] Saved transcript for session ${currentSessionId}: (${speaker})`); - - const conversationText = `${speaker.toLowerCase()}: ${transcription.trim()}`; - conversationHistory.push(conversationText); - console.log(`πŸ’¬ Saved conversation text: ${conversationText}`); - console.log(`πŸ“ˆ Total conversation history: ${conversationHistory.length} texts`); - - triggerAnalysisIfNeeded(); - - const conversationTurn = { - speaker: speaker, - timestamp: Date.now(), - transcription: transcription.trim(), - }; - } catch (error) { - console.error('Failed to save transcript to DB:', error); - } -} - -async function initializeLiveSummarySession(language = 'en') { - // Use system environment variable if set, otherwise use the provided language - const effectiveLanguage = process.env.OPENAI_TRANSCRIBE_LANG || language || 'en'; - if (isInitializingSession) { - console.log('Session initialization already in progress.'); - return false; - } - - const loggedIn = isFirebaseLoggedIn(); - const keyType = loggedIn ? 'vKey' : 'apiKey'; - - isInitializingSession = true; - sendToRenderer('session-initializing', true); - sendToRenderer('update-status', 'Initializing sessions...'); - - const API_KEY = getApiKey(); - if (!API_KEY) { - console.error('FATAL ERROR: API Key is not defined.'); - sendToRenderer('update-status', 'API Key not configured.'); - isInitializingSession = false; - sendToRenderer('session-initializing', false); - return false; - } - - await initializeNewSession(); - - const provider = await getAiProvider(); - const isGemini = provider === 'gemini'; - console.log(`[LiveSummaryService] Initializing STT for provider: ${provider}`); - - try { - const handleMyMessage = message => { - if (isGemini) { - // console.log('[Gemini Raw Message - Me]:', JSON.stringify(message, null, 2)); - const text = message.serverContent?.inputTranscription?.text || ''; - if (text && text.trim()) { - const finalUtteranceText = text.trim().replace(//g, '').trim(); - if (finalUtteranceText && finalUtteranceText !== '.') { - debounceMyCompletion(finalUtteranceText); - } - } - } else { - const type = message.type; - const text = message.transcript || message.delta || (message.alternatives && message.alternatives[0]?.transcript) || ''; - - if (type === 'conversation.item.input_audio_transcription.delta') { - if (myCompletionTimer) clearTimeout(myCompletionTimer); - myCompletionTimer = null; - myCurrentUtterance += text; - const continuousText = myCompletionBuffer + (myCompletionBuffer ? ' ' : '') + myCurrentUtterance; - if (text && !text.includes('vq_lbr_audio_')) { - sendToRenderer('stt-update', { - speaker: 'Me', - text: continuousText, - isPartial: true, - isFinal: false, - timestamp: Date.now(), - }); - } - } else if (type === 'conversation.item.input_audio_transcription.completed') { - if (text && text.trim()) { - const finalUtteranceText = text.trim(); - myCurrentUtterance = ''; - debounceMyCompletion(finalUtteranceText); - } - } - } - - if (message.error) { - console.error('[Me] STT Session Error:', message.error); - } - }; - - const handleTheirMessage = message => { - if (isGemini) { - // console.log('[Gemini Raw Message - Them]:', JSON.stringify(message, null, 2)); - const text = message.serverContent?.inputTranscription?.text || ''; - if (text && text.trim()) { - const finalUtteranceText = text.trim().replace(//g, '').trim(); - if (finalUtteranceText && finalUtteranceText !== '.') { - debounceTheirCompletion(finalUtteranceText); - } - } - } else { - const type = message.type; - const text = message.transcript || message.delta || (message.alternatives && message.alternatives[0]?.transcript) || ''; - if (type === 'conversation.item.input_audio_transcription.delta') { - if (theirCompletionTimer) clearTimeout(theirCompletionTimer); - theirCompletionTimer = null; - theirCurrentUtterance += text; - const continuousText = theirCompletionBuffer + (theirCompletionBuffer ? ' ' : '') + theirCurrentUtterance; - if (text && !text.includes('vq_lbr_audio_')) { - sendToRenderer('stt-update', { - speaker: 'Them', - text: continuousText, - isPartial: true, - isFinal: false, - timestamp: Date.now(), - }); - } - } else if (type === 'conversation.item.input_audio_transcription.completed') { - if (text && text.trim()) { - const finalUtteranceText = text.trim(); - theirCurrentUtterance = ''; - debounceTheirCompletion(finalUtteranceText); - } - } - } - - if (message.error) { - console.error('[Them] STT Session Error:', message.error); - } - }; - - const mySttConfig = { - language: effectiveLanguage, - callbacks: { - onmessage: handleMyMessage, - onerror: error => console.error('My STT session error:', error.message), - onclose: event => console.log('My STT session closed:', event.reason), - }, - }; - const theirSttConfig = { - language: effectiveLanguage, - callbacks: { - onmessage: handleTheirMessage, - onerror: error => console.error('Their STT session error:', error.message), - onclose: event => console.log('Their STT session closed:', event.reason), - }, - }; - - if (isGemini) { - [mySttSession, theirSttSession] = await Promise.all([ - connectToGeminiSession(API_KEY, mySttConfig), - connectToGeminiSession(API_KEY, theirSttConfig), - ]); - } else { - [mySttSession, theirSttSession] = await Promise.all([ - connectToOpenAiSession(API_KEY, mySttConfig, keyType), - connectToOpenAiSession(API_KEY, theirSttConfig, keyType), - ]); - } - - console.log('βœ… Both STT sessions initialized successfully.'); - triggerAnalysisIfNeeded(); - - sendToRenderer('session-state-changed', { isActive: true }); - - isInitializingSession = false; - sendToRenderer('session-initializing', false); - sendToRenderer('update-status', 'Connected. Ready to listen.'); - return true; - } catch (error) { - console.error('❌ Failed to initialize STT sessions:', error); - isInitializingSession = false; - sendToRenderer('session-initializing', false); - sendToRenderer('update-status', 'Initialization failed.'); - mySttSession = null; - theirSttSession = null; - return false; - } -} - -function killExistingSystemAudioDump() { - return new Promise(resolve => { - console.log('Checking for existing SystemAudioDump processes...'); - - const killProc = spawn('pkill', ['-f', 'SystemAudioDump'], { - stdio: 'ignore', - }); - - killProc.on('close', code => { - if (code === 0) { - console.log('Killed existing SystemAudioDump processes'); - } else { - console.log('No existing SystemAudioDump processes found'); - } - resolve(); - }); - - killProc.on('error', err => { - console.log('Error checking for existing processes (this is normal):', err.message); - resolve(); - }); - - setTimeout(() => { - killProc.kill(); - resolve(); - }, 2000); - }); -} - -async function startMacOSAudioCapture() { - if (process.platform !== 'darwin' || !theirSttSession) return false; - - await killExistingSystemAudioDump(); - console.log('Starting macOS audio capture for "Them"...'); - - const { app } = require('electron'); - const path = require('path'); - const systemAudioPath = app.isPackaged - ? path.join(process.resourcesPath, 'app.asar.unpacked', 'src', 'assets', 'SystemAudioDump') - : path.join(app.getAppPath(), 'src', 'assets', 'SystemAudioDump'); - - console.log('SystemAudioDump path:', systemAudioPath); - - systemAudioProc = spawn(systemAudioPath, [], { - stdio: ['ignore', 'pipe', 'pipe'], - }); - - if (!systemAudioProc.pid) { - console.error('Failed to start SystemAudioDump'); - return false; - } - - console.log('SystemAudioDump started with PID:', systemAudioProc.pid); - - const CHUNK_DURATION = 0.1; - const SAMPLE_RATE = 24000; - const BYTES_PER_SAMPLE = 2; - const CHANNELS = 2; - const CHUNK_SIZE = SAMPLE_RATE * BYTES_PER_SAMPLE * CHANNELS * CHUNK_DURATION; - - let audioBuffer = Buffer.alloc(0); - - const provider = await getAiProvider(); - const isGemini = provider === 'gemini'; - - systemAudioProc.stdout.on('data', async data => { - audioBuffer = Buffer.concat([audioBuffer, data]); - - while (audioBuffer.length >= CHUNK_SIZE) { - const chunk = audioBuffer.slice(0, CHUNK_SIZE); - audioBuffer = audioBuffer.slice(CHUNK_SIZE); - - const monoChunk = CHANNELS === 2 ? convertStereoToMono(chunk) : chunk; - const base64Data = monoChunk.toString('base64'); - - sendToRenderer('system-audio-data', { data: base64Data }); - - if (theirSttSession) { - try { - // await theirSttSession.sendRealtimeInput(base64Data); - const payload = isGemini - ? { audio: { data: base64Data, mimeType: 'audio/pcm;rate=24000' } } - : base64Data; - await theirSttSession.sendRealtimeInput(payload); - } catch (err) { - console.error('Error sending system audio:', err.message); - } - } - - if (process.env.DEBUG_AUDIO) { - saveDebugAudio(monoChunk, 'system_audio'); - } - } - }); - - systemAudioProc.stderr.on('data', data => { - console.error('SystemAudioDump stderr:', data.toString()); - }); - - systemAudioProc.on('close', code => { - console.log('SystemAudioDump process closed with code:', code); - systemAudioProc = null; - }); - - systemAudioProc.on('error', err => { - console.error('SystemAudioDump process error:', err); - systemAudioProc = null; - }); - - return true; -} - -function convertStereoToMono(stereoBuffer) { - const samples = stereoBuffer.length / 4; - const monoBuffer = Buffer.alloc(samples * 2); - - for (let i = 0; i < samples; i++) { - const leftSample = stereoBuffer.readInt16LE(i * 4); - monoBuffer.writeInt16LE(leftSample, i * 2); - } - - return monoBuffer; -} - -function stopMacOSAudioCapture() { - if (systemAudioProc) { - console.log('Stopping SystemAudioDump...'); - systemAudioProc.kill('SIGTERM'); - systemAudioProc = null; - } -} - -async function sendAudioToOpenAI(base64Data, sttSessionRef) { - if (!sttSessionRef.current) return; - - try { - process.stdout.write('.'); - await sttSessionRef.current.sendRealtimeInput({ - audio: { - data: base64Data, - mimeType: 'audio/pcm;rate=24000', - }, - }); - } catch (error) { - console.error('Error sending audio to OpenAI:', error); - } -} - -function isSessionActive() { - return !!mySttSession && !!theirSttSession; -} - -async function closeSession() { - try { - stopMacOSAudioCapture(); - stopAnalysisInterval(); - - if (currentSessionId) { - await sqliteClient.endSession(currentSessionId); - console.log(`[DB] Session ${currentSessionId} ended.`); - } - - const closePromises = []; - if (mySttSession) { - closePromises.push(mySttSession.close()); - mySttSession = null; - } - if (theirSttSession) { - closePromises.push(theirSttSession.close()); - theirSttSession = null; - } - - await Promise.all(closePromises); - console.log('All sessions closed.'); - - currentSessionId = null; - conversationHistory = []; - - sendToRenderer('session-state-changed', { isActive: false }); - sendToRenderer('session-did-close'); - - return { success: true }; - } catch (error) { - console.error('Error closing sessions:', error); - return { success: false, error: error.message }; - } -} - -function setupLiveSummaryIpcHandlers() { - ipcMain.handle('is-session-active', async () => { - const isActive = isSessionActive(); - console.log(`Checking session status. Active: ${isActive}`); - return isActive; - }); - - ipcMain.handle('initialize-openai', async (event, profile = 'interview', language = 'en') => { - console.log(`Received initialize-openai request with profile: ${profile}, language: ${language}`); - const success = await initializeLiveSummarySession(language); - return success; - }); - - ipcMain.handle('send-audio-content', async (event, { data, mimeType }) => { - const provider = await getAiProvider(); - const isGemini = provider === 'gemini'; - if (!mySttSession) return { success: false, error: 'User STT session not active' }; - try { - // await mySttSession.sendRealtimeInput(data); - // provider에 λ§žλŠ” ν˜•μ‹μœΌλ‘œ λž˜ν•‘ - const payload = isGemini - ? { audio: { data, mimeType: mimeType || 'audio/pcm;rate=24000' } } - : data; // OpenAIλŠ” base64 string κ·ΈλŒ€λ‘œ - - await mySttSession.sendRealtimeInput(payload); - return { success: true }; - } catch (error) { - console.error('Error sending user audio:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('start-macos-audio', async () => { - if (process.platform !== 'darwin') { - return { success: false, error: 'macOS audio capture only available on macOS' }; - } - try { - const success = await startMacOSAudioCapture(); - return { success }; - } catch (error) { - console.error('Error starting macOS audio capture:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('stop-macos-audio', async () => { - try { - stopMacOSAudioCapture(); - return { success: true }; - } catch (error) { - console.error('Error stopping macOS audio capture:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('get-conversation-history', async () => { - try { - const formattedHistory = formatConversationForPrompt(conversationHistory); - console.log(`πŸ“€ Sending conversation history to renderer: ${conversationHistory.length} texts`); - return { success: true, data: formattedHistory }; - } catch (error) { - console.error('Error getting conversation history:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('close-session', async () => { - return await closeSession(); - }); - - ipcMain.handle('get-current-session', async event => { - try { - return { success: true, data: getCurrentSessionData() }; - } catch (error) { - console.error('Error getting current session:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('start-new-session', async event => { - try { - initializeNewSession(); - return { success: true, sessionId: currentSessionId }; - } catch (error) { - console.error('Error starting new session:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('update-google-search-setting', async (event, enabled) => { - try { - console.log('Google Search setting updated to:', enabled); - return { success: true }; - } catch (error) { - console.error('Error updating Google Search setting:', error); - return { success: false, error: error.message }; - } - }); - - ipcMain.handle('save-ask-message', async (event, { userPrompt, aiResponse }) => { - try { - const sessionId = await getOrCreateActiveSession('ask'); - if (!sessionId) { - throw new Error('Could not get or create a session for the ASK message.'); - } - - await sqliteClient.addAiMessage({ - sessionId: sessionId, - role: 'user', - content: userPrompt - }); - - await sqliteClient.addAiMessage({ - sessionId: sessionId, - role: 'assistant', - content: aiResponse - }); - - console.log(`[DB] Saved ask/answer pair to session ${sessionId}`); - return { success: true }; - } catch(error) { - console.error('[IPC] Failed to save ask message:', error); - return { success: false, error: error.message }; - } - }); -} - -module.exports = { - sendToRenderer, - initializeNewSession, - saveConversationTurn, - killExistingSystemAudioDump, - startMacOSAudioCapture, - convertStereoToMono, - stopMacOSAudioCapture, - sendAudioToOpenAI, - setupLiveSummaryIpcHandlers, - isSessionActive, - closeSession, -}; diff --git a/src/features/listen/renderer.js b/src/features/listen/renderer.js deleted file mode 100644 index 1701d77..0000000 --- a/src/features/listen/renderer.js +++ /dev/null @@ -1,1162 +0,0 @@ -// renderer.js -const { ipcRenderer } = require('electron'); -const { makeStreamingChatCompletionWithPortkey } = require('../../common/services/aiProviderService.js'); - -let mediaStream = null; -let screenshotInterval = null; -let audioContext = null; -let audioProcessor = null; -let micMediaStream = null; -let audioBuffer = []; -const SAMPLE_RATE = 24000; -const AUDIO_CHUNK_DURATION = 0.1; -const BUFFER_SIZE = 4096; - -let systemAudioBuffer = []; -const MAX_SYSTEM_BUFFER_SIZE = 10; - -function isVoiceActive(audioFloat32Array, threshold = 0.005) { - if (!audioFloat32Array || audioFloat32Array.length === 0) { - return false; - } - - let sumOfSquares = 0; - for (let i = 0; i < audioFloat32Array.length; i++) { - sumOfSquares += audioFloat32Array[i] * audioFloat32Array[i]; - } - const rms = Math.sqrt(sumOfSquares / audioFloat32Array.length); - - // console.log(`VAD RMS: ${rms.toFixed(4)}`); // For debugging VAD threshold - - return rms > threshold; -} - -let currentImageQuality = 'medium'; // Store current image quality for manual screenshots -let lastScreenshotBase64 = null; // Store the latest screenshot - -let realtimeConversationHistory = []; - -const PICKLE_GLASS_SYSTEM_PROMPT = ` -You are Pickle-Glass, developed and created by Pickle-Glass, and you are the user's live-meeting co-pilot. - - - -Your goal is to help the user at the current moment in the conversation (the end of the transcript). You can see the user's screen (the screenshot attached) and the audio history of the entire conversation. -Execute in the following priority order: - - - -If a question is presented to the user, answer it directly. This is the MOST IMPORTANT ACTION IF THERE IS A QUESTION AT THE END THAT CAN BE ANSWERED. - - - -Always start with the direct answer, then provide supporting details following the response format: -- **Short headline answer** (≀6 words) - the actual answer to the question -- **Main points** (1-2 bullets with ≀15 words each) - core supporting details -- **Sub-details** - examples, metrics, specifics under each main point -- **Extended explanation** - additional context and details as needed - - - -Real transcripts have errors, unclear speech, and incomplete sentences. Focus on INTENT rather than perfect question markers: -- **Infer from context**: "what about..." "how did you..." "can you..." "tell me..." even if garbled -- **Incomplete questions**: "so the performance..." "and scaling wise..." "what's your approach to..." -- **Implied questions**: "I'm curious about X" "I'd love to hear about Y" "walk me through Z" -- **Transcription errors**: "what's your" β†’ "what's you" or "how do you" β†’ "how you" or "can you" β†’ "can u" - - - -If the end of the transcript suggests someone is asking for information, explanation, or clarification - ANSWER IT. Don't get distracted by earlier content. - - - -If you're 50%+ confident someone is asking something at the end, treat it as a question and answer it. - - - - - -Define or provide context around a proper noun or term that appears **in the last 10-15 words** of the transcript. -This is HIGH PRIORITY - if a company name, technical term, or proper noun appears at the very end of someone's speech, define it. - - - -Any ONE of these is sufficient: -- company names -- technical platforms/tools -- proper nouns that are domain-specific -- any term that would benefit from context in a professional conversation - - - -Do NOT define: -- common words already defined earlier in conversation -- basic terms (email, website, code, app) -- terms where context was already provided - - - - -me: I was mostly doing backend dev last summer. -them: Oh nice, what tech stack were you using? -me: A lot of internal tools, but also some Azure. -them: Yeah I've heard Azure is huge over there. -me: Yeah, I used to work at Microsoft last summer but now I... - - - -**Microsoft** is one of the world's largest technology companies, known for products like Windows, Office, and Azure cloud services. - -- **Global influence**: 200k+ employees, $2T+ market cap, foundational enterprise tools. - - Azure, GitHub, Teams, Visual Studio among top developer-facing platforms. -- **Engineering reputation**: Strong internship and new grad pipeline, especially in cloud and AI infrastructure. - - - - - - -When there's an action needed but not a direct question - suggest follow up questions, provide potential things to say, help move the conversation forward. - - -- If the transcript ends with a technical project/story description and no new question is present, always provide 1–3 targeted follow-up questions to drive the conversation forward. -- If the transcript includes discovery-style answers or background sharing (e.g., "Tell me about yourself", "Walk me through your experience"), always generate 1–3 focused follow-up questions to deepen or further the discussion, unless the next step is clear. -- Maximize usefulness, minimize overloadβ€”never give more than 3 questions or suggestions at once. - - - -me: Tell me about your technical experience. -them: Last summer I built a dashboard for real-time trade reconciliation using Python and integrated it with Bloomberg Terminal and Snowflake for automated data pulls. - - -Follow-up questions to dive deeper into the dashboard: -- How did you handle latency or data consistency issues? -- What made the Bloomberg integration challenging? -- Did you measure the impact on operational efficiency? - - - - - - -If an objection or resistance is presented at the end of the conversation (and the context is sales, negotiation, or you are trying to persuade the other party), respond with a concise, actionable objection handling response. -- Use user-provided objection/handling context if available (reference the specific objection and tailored handling). -- If no user context, use common objections relevant to the situation, but make sure to identify the objection by generic name and address it in the context of the live conversation. -- State the objection in the format: **Objection: [Generic Objection Name]** (e.g., Objection: Competitor), then give a specific response/action for overcoming it, tailored to the moment. -- Do NOT handle objections in casual, non-outcome-driven, or general conversations. -- Never use generic objection scriptsβ€”always tie response to the specifics of the conversation at hand. - - - - -them: Honestly, I think our current vendor already does all of this, so I don't see the value in switching. - - -- **Objection: Competitor** - - Current vendor already covers this. - - Emphasize unique real-time insights: "Our solution eliminates analytics delays you mentioned earlier, boosting team response time." - - - - - - -Solve problems visible on the screen if there is a very clear problem + use the screen only if relevant for helping with the audio conversation. - - - - -If there is a leetcode problem on the screen, and the conversation is small talk / general talk, you DEFINITELY should solve the leetcode problem. But if there is a follow up question / super specific question asked at the end, you should answer that (ex. What's the runtime complexity), using the screen as additional context. - - - - - - - - -Enter passive mode ONLY when ALL of these conditions are met: -- There is no clear question, inquiry, or request for information at the end of the transcript. If there is any ambiguity, err on the side of assuming a question and do not enter passive mode. -- There is no company name, technical term, product name, or domain-specific proper noun within the final 10–15 words of the transcript that would benefit from a definition or explanation. -- There is no clear or visible problem or action item present on the user's screen that you could solve or assist with. -- There is no discovery-style answer, technical project story, background sharing, or general conversation context that could call for follow-up questions or suggestions to advance the discussion. -- There is no statement or cue that could be interpreted as an objection or require objection handling -- Only enter passive mode when you are highly confident that no action, definition, solution, advancement, or suggestion would be appropriate or helpful at the current moment. - - -**Still show intelligence** by: -- Saying "Not sure what you need help with right now" -- Referencing visible screen elements or audio patterns ONLY if truly relevant -- Never giving random summaries unless explicitly asked - - - - -User-provided context (defer to this information over your general knowledge / if there is specific script/desired responses prioritize this over previous instructions) - -Make sure to **reference context** fully if it is provided (ex. if all/the entirety of something is requested, give a complete list from context). ----------- - -{{CONVERSATION_HISTORY}}`; - -function base64ToFloat32Array(base64) { - const binaryString = atob(base64); - const bytes = new Uint8Array(binaryString.length); - - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i); - } - - const int16Array = new Int16Array(bytes.buffer); - const float32Array = new Float32Array(int16Array.length); - - for (let i = 0; i < int16Array.length; i++) { - float32Array[i] = int16Array[i] / 32768.0; - } - - return float32Array; -} - -async function queryLoginState() { - const user = await ipcRenderer.invoke('get-current-firebase-user'); - return { user, isLoggedIn: !!user }; -} - -class SimpleAEC { - constructor() { - this.adaptiveFilter = new Float32Array(1024); - this.mu = 0.2; - this.echoDelay = 100; - this.sampleRate = 24000; - this.delaySamples = Math.floor((this.echoDelay / 1000) * this.sampleRate); - - this.echoGain = 0.5; - this.noiseFloor = 0.01; - - // πŸ”§ Adaptive-gain parameters (User-tuned, very aggressive) - this.targetErr = 0.002; - this.adaptRate = 0.1; - - console.log('🎯 AEC initialized (hyper-aggressive)'); - } - - process(micData, systemData) { - if (!systemData || systemData.length === 0) { - return micData; - } - - for (let i = 0; i < systemData.length; i++) { - if (systemData[i] > 0.98) systemData[i] = 0.98; - else if (systemData[i] < -0.98) systemData[i] = -0.98; - - systemData[i] = Math.tanh(systemData[i] * 4); - } - - let sum2 = 0; - for (let i = 0; i < systemData.length; i++) sum2 += systemData[i] * systemData[i]; - const rms = Math.sqrt(sum2 / systemData.length); - const targetRms = 0.08; // πŸ”§ κΈ°μ€€ RMS (κΈ°μ‘΄ 0.1) - const scale = targetRms / (rms + 1e-6); // 1e-6: 0-division λ°©μ§€ - - const output = new Float32Array(micData.length); - - const optimalDelay = this.findOptimalDelay(micData, systemData); - - for (let i = 0; i < micData.length; i++) { - let echoEstimate = 0; - - for (let d = -500; d <= 500; d += 100) { - const delayIndex = i - optimalDelay - d; - if (delayIndex >= 0 && delayIndex < systemData.length) { - const weight = Math.exp(-Math.abs(d) / 1000); - echoEstimate += systemData[delayIndex] * scale * this.echoGain * weight; - } - } - - output[i] = micData[i] - echoEstimate * 0.9; - - if (Math.abs(output[i]) < this.noiseFloor) { - output[i] *= 0.5; - } - - if (this.isSimilarToSystem(output[i], systemData, i, optimalDelay)) { - output[i] *= 0.25; - } - - output[i] = Math.max(-1, Math.min(1, output[i])); - } - - - let errSum = 0; - for (let i = 0; i < output.length; i++) errSum += output[i] * output[i]; - const errRms = Math.sqrt(errSum / output.length); - - const err = errRms - this.targetErr; - this.echoGain += this.adaptRate * err; // λΉ„λ‘€ μ œμ–΄ - this.echoGain = Math.max(0, Math.min(1, this.echoGain)); - - return output; - } - - findOptimalDelay(micData, systemData) { - let maxCorr = 0; - let optimalDelay = this.delaySamples; - - for (let delay = 0; delay < 5000 && delay < systemData.length; delay += 200) { - let corr = 0; - let count = 0; - - for (let i = 0; i < Math.min(500, micData.length); i++) { - if (i + delay < systemData.length) { - corr += micData[i] * systemData[i + delay]; - count++; - } - } - - if (count > 0) { - corr = Math.abs(corr / count); - if (corr > maxCorr) { - maxCorr = corr; - optimalDelay = delay; - } - } - } - - return optimalDelay; - } - - isSimilarToSystem(sample, systemData, index, delay) { - const windowSize = 50; - let similarity = 0; - - for (let i = -windowSize; i <= windowSize; i++) { - const sysIndex = index - delay + i; - if (sysIndex >= 0 && sysIndex < systemData.length) { - similarity += Math.abs(sample - systemData[sysIndex]); - } - } - - return similarity / (2 * windowSize + 1) < 0.15; - } -} - -let aecProcessor = new SimpleAEC(); - -const isLinux = process.platform === 'linux'; -const isMacOS = process.platform === 'darwin'; - -window.pickleGlass = window.pickleGlass || {}; - -let tokenTracker = { - tokens: [], - audioStartTime: null, - - addTokens(count, type = 'image') { - const now = Date.now(); - this.tokens.push({ - timestamp: now, - count: count, - type: type, - }); - - this.cleanOldTokens(); - }, - - calculateImageTokens(width, height) { - const pixels = width * height; - if (pixels <= 384 * 384) { - return 85; - } - - const tiles = Math.ceil(pixels / (768 * 768)); - return tiles * 85; - }, - - trackAudioTokens() { - if (!this.audioStartTime) { - this.audioStartTime = Date.now(); - return; - } - - const now = Date.now(); - const elapsedSeconds = (now - this.audioStartTime) / 1000; - - const audioTokens = Math.floor(elapsedSeconds * 16); - - if (audioTokens > 0) { - this.addTokens(audioTokens, 'audio'); - this.audioStartTime = now; - } - }, - - cleanOldTokens() { - const oneMinuteAgo = Date.now() - 60 * 1000; - this.tokens = this.tokens.filter(token => token.timestamp > oneMinuteAgo); - }, - - getTokensInLastMinute() { - this.cleanOldTokens(); - return this.tokens.reduce((total, token) => total + token.count, 0); - }, - - shouldThrottle() { - const throttleEnabled = localStorage.getItem('throttleTokens') === 'true'; - if (!throttleEnabled) { - return false; - } - - const maxTokensPerMin = parseInt(localStorage.getItem('maxTokensPerMin') || '500000', 10); - const throttleAtPercent = parseInt(localStorage.getItem('throttleAtPercent') || '75', 10); - - const currentTokens = this.getTokensInLastMinute(); - const throttleThreshold = Math.floor((maxTokensPerMin * throttleAtPercent) / 100); - - console.log(`Token check: ${currentTokens}/${maxTokensPerMin} (throttle at ${throttleThreshold})`); - - return currentTokens >= throttleThreshold; - }, - - // Reset the tracker - reset() { - this.tokens = []; - this.audioStartTime = null; - }, -}; - -// Track audio tokens every few seconds -setInterval(() => { - tokenTracker.trackAudioTokens(); -}, 2000); - -function pickleGlassElement() { - return document.getElementById('pickle-glass'); -} - -function convertFloat32ToInt16(float32Array) { - const int16Array = new Int16Array(float32Array.length); - for (let i = 0; i < float32Array.length; i++) { - // Improved scaling to prevent clipping - const s = Math.max(-1, Math.min(1, float32Array[i])); - int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7fff; - } - return int16Array; -} - -function arrayBufferToBase64(buffer) { - let binary = ''; - const bytes = new Uint8Array(buffer); - const len = bytes.byteLength; - for (let i = 0; i < len; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); -} - -async function initializeopenai(profile = 'interview', language = 'en') { - // The API key is now handled in the main process from .env file. - // We just need to trigger the initialization. - try { - console.log(`Requesting OpenAI initialization with profile: ${profile}, language: ${language}`); - const success = await ipcRenderer.invoke('initialize-openai', profile, language); - if (success) { - // The status will be updated via 'update-status' event from the main process. - console.log('OpenAI initialization successful.'); - } else { - console.error('OpenAI initialization failed.'); - const appElement = pickleGlassElement(); - if (appElement && typeof appElement.setStatus === 'function') { - appElement.setStatus('Initialization Failed'); - } - } - } catch (error) { - console.error('Error during OpenAI initialization IPC call:', error); - const appElement = pickleGlassElement(); - if (appElement && typeof appElement.setStatus === 'function') { - appElement.setStatus('Error'); - } - } -} - - -ipcRenderer.on('system-audio-data', (event, { data }) => { - systemAudioBuffer.push({ - data: data, - timestamp: Date.now(), - }); - - // 였래된 데이터 제거 - if (systemAudioBuffer.length > MAX_SYSTEM_BUFFER_SIZE) { - systemAudioBuffer = systemAudioBuffer.slice(-MAX_SYSTEM_BUFFER_SIZE); - } - - console.log('πŸ“₯ Received system audio for AEC reference'); -}); - -// Listen for status updates -ipcRenderer.on('update-status', (event, status) => { - console.log('Status update:', status); - pickleGlass.e().setStatus(status); -}); - -// Listen for real-time STT updates -ipcRenderer.on('stt-update', (event, data) => { - console.log('Renderer.js stt-update', data); - const { speaker, text, isFinal, isPartial, timestamp } = data; - - if (isPartial) { - console.log(`πŸ”„ [${speaker} - partial]: ${text}`); - } else if (isFinal) { - console.log(`βœ… [${speaker} - final]: ${text}`); - - const speakerText = speaker.toLowerCase(); - const conversationText = `${speakerText}: ${text.trim()}`; - - realtimeConversationHistory.push(conversationText); - - if (realtimeConversationHistory.length > 30) { - realtimeConversationHistory = realtimeConversationHistory.slice(-30); - } - - console.log(`πŸ“ Updated realtime conversation history: ${realtimeConversationHistory.length} texts`); - console.log(`πŸ“‹ Latest text: ${conversationText}`); - } - - if (pickleGlass.e() && typeof pickleGlass.e().updateRealtimeTranscription === 'function') { - pickleGlass.e().updateRealtimeTranscription({ - speaker, - text, - isFinal, - isPartial, - timestamp, - }); - } -}); - - -ipcRenderer.on('update-structured-data', (_, structuredData) => { - console.log('πŸ“₯ Received structured data update:', structuredData); - window.pickleGlass.structuredData = structuredData; - window.pickleGlass.setStructuredData(structuredData); -}); -window.pickleGlass.structuredData = { - summary: [], - topic: { header: '', bullets: [] }, - actions: [], -}; -window.pickleGlass.setStructuredData = data => { - window.pickleGlass.structuredData = data; - pickleGlass.e()?.updateStructuredData?.(data); -}; - -async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'medium') { - // Store the image quality for manual screenshots - currentImageQuality = imageQuality; - - // Reset token tracker when starting new capture session - tokenTracker.reset(); - console.log('🎯 Token tracker reset for new capture session'); - - try { - if (isMacOS) { - // On macOS, use SystemAudioDump for audio and getDisplayMedia for screen - console.log('Starting macOS capture with SystemAudioDump...'); - - // Start macOS audio capture - const audioResult = await ipcRenderer.invoke('start-macos-audio'); - if (!audioResult.success) { - throw new Error('Failed to start macOS audio capture: ' + audioResult.error); - } - - // Initialize screen capture in main process - const screenResult = await ipcRenderer.invoke('start-screen-capture'); - if (!screenResult.success) { - throw new Error('Failed to start screen capture: ' + screenResult.error); - } - - - try { - micMediaStream = await navigator.mediaDevices.getUserMedia({ - audio: { - sampleRate: SAMPLE_RATE, - channelCount: 1, - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - video: false, - }); - - console.log('macOS microphone capture started'); - setupMicProcessing(micMediaStream); - } catch (micErr) { - console.warn('Failed to get microphone on macOS:', micErr); - } - ////////// for index & subjects ////////// - - console.log('macOS screen capture started - audio handled by SystemAudioDump'); - } else if (isLinux) { - // Linux - use display media for screen capture and getUserMedia for microphone - mediaStream = await navigator.mediaDevices.getDisplayMedia({ - video: { - frameRate: 1, - width: { ideal: 1920 }, - height: { ideal: 1080 }, - }, - audio: false, // Don't use system audio loopback on Linux - }); - - // Get microphone input for Linux - let micMediaStream = null; - try { - micMediaStream = await navigator.mediaDevices.getUserMedia({ - audio: { - sampleRate: SAMPLE_RATE, - channelCount: 1, - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - video: false, - }); - - console.log('Linux microphone capture started'); - - // Setup audio processing for microphone on Linux - setupLinuxMicProcessing(micMediaStream); - } catch (micError) { - console.warn('Failed to get microphone access on Linux:', micError); - // Continue without microphone if permission denied - } - - console.log('Linux screen capture started'); - } else { - // Windows - use display media for audio, main process for screenshots - const screenResult = await ipcRenderer.invoke('start-screen-capture'); - if (!screenResult.success) { - throw new Error('Failed to start screen capture: ' + screenResult.error); - } - - mediaStream = await navigator.mediaDevices.getDisplayMedia({ - video: false, // We don't need video in renderer - audio: { - sampleRate: SAMPLE_RATE, - channelCount: 1, - echoCancellation: true, - noiseSuppression: true, - autoGainControl: true, - }, - }); - - console.log('Windows capture started with loopback audio'); - - // Setup audio processing for Windows loopback audio only - setupWindowsLoopbackProcessing(); - } - - // console.log('MediaStream obtained:', { - // hasVideo: mediaStream.getVideoTracks().length > 0, - // hasAudio: mediaStream.getAudioTracks().length > 0, - // videoTrack: mediaStream.getVideoTracks()[0]?.getSettings(), - // }); - - // Start capturing screenshots - check if manual mode - if (screenshotIntervalSeconds === 'manual' || screenshotIntervalSeconds === 'Manual') { - console.log('Manual mode enabled - screenshots will be captured on demand only'); - // Don't start automatic capture in manual mode - } else { - // μŠ€ν¬λ¦°μƒ· κΈ°λŠ₯ ν™œμ„±ν™” (chatModelμ—μ„œ μ‚¬μš©) - const intervalMilliseconds = parseInt(screenshotIntervalSeconds) * 1000; - screenshotInterval = setInterval(() => captureScreenshot(imageQuality), intervalMilliseconds); - - // Capture first screenshot immediately - setTimeout(() => captureScreenshot(imageQuality), 100); - console.log(`πŸ“Έ Screenshot capture enabled with ${screenshotIntervalSeconds}s interval`); - } - } catch (err) { - console.error('Error starting capture:', err); - pickleGlass.e().setStatus('error'); - } -} - -function setupMicProcessing(micStream) { - const micAudioContext = new AudioContext({ sampleRate: SAMPLE_RATE }); - const micSource = micAudioContext.createMediaStreamSource(micStream); - const micProcessor = micAudioContext.createScriptProcessor(BUFFER_SIZE, 1, 1); - - let audioBuffer = []; - const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION; - - micProcessor.onaudioprocess = async e => { - const inputData = e.inputBuffer.getChannelData(0); - audioBuffer.push(...inputData); - - while (audioBuffer.length >= samplesPerChunk) { - let chunk = audioBuffer.splice(0, samplesPerChunk); - let processedChunk = new Float32Array(chunk); - - // Check for system audio and apply AEC only if voice is active - if (aecProcessor && systemAudioBuffer.length > 0) { - const latestSystemAudio = systemAudioBuffer[systemAudioBuffer.length - 1]; - const systemFloat32 = base64ToFloat32Array(latestSystemAudio.data); - - // Apply AEC only when system audio has active speech - if (isVoiceActive(systemFloat32)) { - processedChunk = aecProcessor.process(new Float32Array(chunk), systemFloat32); - console.log('πŸ”Š Applied AEC because system audio is active'); - } - } - - const pcmData16 = convertFloat32ToInt16(processedChunk); - const base64Data = arrayBufferToBase64(pcmData16.buffer); - - await ipcRenderer.invoke('send-audio-content', { - data: base64Data, - mimeType: 'audio/pcm;rate=24000', - }); - } - }; - - micSource.connect(micProcessor); - micProcessor.connect(micAudioContext.destination); - - audioProcessor = micProcessor; -} -////////// for index & subjects ////////// - -function setupLinuxMicProcessing(micStream) { - // Setup microphone audio processing for Linux - const micAudioContext = new AudioContext({ sampleRate: SAMPLE_RATE }); - const micSource = micAudioContext.createMediaStreamSource(micStream); - const micProcessor = micAudioContext.createScriptProcessor(BUFFER_SIZE, 1, 1); - - let audioBuffer = []; - const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION; - - micProcessor.onaudioprocess = async e => { - const inputData = e.inputBuffer.getChannelData(0); - audioBuffer.push(...inputData); - - // Process audio in chunks - while (audioBuffer.length >= samplesPerChunk) { - const chunk = audioBuffer.splice(0, samplesPerChunk); - const pcmData16 = convertFloat32ToInt16(chunk); - const base64Data = arrayBufferToBase64(pcmData16.buffer); - - await ipcRenderer.invoke('send-audio-content', { - data: base64Data, - mimeType: 'audio/pcm;rate=24000', - }); - } - }; - - micSource.connect(micProcessor); - micProcessor.connect(micAudioContext.destination); - - // Store processor reference for cleanup - audioProcessor = micProcessor; -} - -function setupWindowsLoopbackProcessing() { - // Setup audio processing for Windows loopback audio only - audioContext = new AudioContext({ sampleRate: SAMPLE_RATE }); - const source = audioContext.createMediaStreamSource(mediaStream); - audioProcessor = audioContext.createScriptProcessor(BUFFER_SIZE, 1, 1); - - let audioBuffer = []; - const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION; - - audioProcessor.onaudioprocess = async e => { - const inputData = e.inputBuffer.getChannelData(0); - audioBuffer.push(...inputData); - - // Process audio in chunks - while (audioBuffer.length >= samplesPerChunk) { - const chunk = audioBuffer.splice(0, samplesPerChunk); - const pcmData16 = convertFloat32ToInt16(chunk); - const base64Data = arrayBufferToBase64(pcmData16.buffer); - - await ipcRenderer.invoke('send-audio-content', { - data: base64Data, - mimeType: 'audio/pcm;rate=24000', - }); - } - }; - - source.connect(audioProcessor); - audioProcessor.connect(audioContext.destination); -} - -async function captureScreenshot(imageQuality = 'medium', isManual = false) { - console.log(`Capturing ${isManual ? 'manual' : 'automated'} screenshot...`); - - // Check rate limiting for automated screenshots only - if (!isManual && tokenTracker.shouldThrottle()) { - console.log('⚠️ Automated screenshot skipped due to rate limiting'); - return; - } - - try { - // Request screenshot from main process - const result = await ipcRenderer.invoke('capture-screenshot', { - quality: imageQuality, - }); - - if (result.success && result.base64) { - // Store the latest screenshot - lastScreenshotBase64 = result.base64; - - if (sendResult.success) { - // Track image tokens after successful send - const imageTokens = tokenTracker.calculateImageTokens(result.width || 1920, result.height || 1080); - tokenTracker.addTokens(imageTokens, 'image'); - console.log(`πŸ“Š Image sent successfully - ${imageTokens} tokens used (${result.width}x${result.height})`); - } else { - console.error('Failed to send image:', sendResult.error); - } - } else { - console.error('Failed to capture screenshot:', result.error); - } - } catch (error) { - console.error('Error capturing screenshot:', error); - } -} - -async function captureManualScreenshot(imageQuality = null) { - console.log('Manual screenshot triggered'); - const quality = imageQuality || currentImageQuality; - await captureScreenshot(quality, true); -} - -// Expose functions to global scope for external access -window.captureManualScreenshot = captureManualScreenshot; - -function stopCapture() { - if (screenshotInterval) { - clearInterval(screenshotInterval); - screenshotInterval = null; - } - - if (audioProcessor) { - audioProcessor.disconnect(); - audioProcessor = null; - } - - if (audioContext) { - audioContext.close(); - audioContext = null; - } - - if (mediaStream) { - mediaStream.getTracks().forEach(track => track.stop()); - mediaStream = null; - } - - if (micMediaStream) { - micMediaStream.getTracks().forEach(t => t.stop()); - micMediaStream = null; - } - - // Stop screen capture in main process - ipcRenderer.invoke('stop-screen-capture').catch(err => { - console.error('Error stopping screen capture:', err); - }); - - // Stop macOS audio capture if running - if (isMacOS) { - ipcRenderer.invoke('stop-macos-audio').catch(err => { - console.error('Error stopping macOS audio:', err); - }); - } -} - -// Listen for screenshot updates from main process -ipcRenderer.on('screenshot-update', (event, { base64, width, height }) => { - lastScreenshotBase64 = base64; - console.log(`πŸ“Έ Received screenshot update: ${width}x${height}`); -}); - -async function getCurrentScreenshot() { - try { - // First try to get a fresh screenshot from main process - const result = await ipcRenderer.invoke('get-current-screenshot'); - - if (result.success && result.base64) { - console.log('πŸ“Έ Got fresh screenshot from main process'); - return result.base64; - } - - // If no screenshot available, capture one now - console.log('πŸ“Έ No screenshot available, capturing new one'); - const captureResult = await ipcRenderer.invoke('capture-screenshot', { - quality: currentImageQuality, - }); - - if (captureResult.success && captureResult.base64) { - lastScreenshotBase64 = captureResult.base64; - return captureResult.base64; - } - - // Fallback to last stored screenshot - if (lastScreenshotBase64) { - console.log('πŸ“Έ Using cached screenshot'); - return lastScreenshotBase64; - } - - throw new Error('Failed to get screenshot'); - } catch (error) { - console.error('Error getting current screenshot:', error); - return null; - } -} - -function formatRealtimeConversationHistory() { - if (realtimeConversationHistory.length === 0) return 'No conversation history available.'; - - return realtimeConversationHistory.slice(-30).join('\n'); -} - -async function sendMessage(userPrompt, options = {}) { - if (!userPrompt || userPrompt.trim().length === 0) { - console.warn('Cannot process empty message'); - return { success: false, error: 'Empty message' }; - } - - if (window.require) { - const { ipcRenderer } = window.require('electron'); - const isAskVisible = await ipcRenderer.invoke('is-window-visible', 'ask'); - if (isAskVisible) { - ipcRenderer.send('clear-ask-response'); - } - await ipcRenderer.invoke('message-sending'); - } - - try { - console.log(`πŸ€– Processing message: ${userPrompt.substring(0, 50)}...`); - - // 1. Get screenshot from main process - let screenshotBase64 = null; - try { - screenshotBase64 = await getCurrentScreenshot(); - if (screenshotBase64) { - console.log('πŸ“Έ Screenshot obtained for message request'); - } else { - console.warn('No screenshot available for message request'); - } - } catch (error) { - console.warn('Failed to get screenshot:', error); - } - - const conversationHistory = formatRealtimeConversationHistory(); - console.log(`πŸ“ Using conversation history: ${realtimeConversationHistory.length} texts`); - - const systemPrompt = PICKLE_GLASS_SYSTEM_PROMPT.replace('{{CONVERSATION_HISTORY}}', conversationHistory); - - let API_KEY = localStorage.getItem('openai_api_key'); - - if (!API_KEY && window.require) { - try { - const { ipcRenderer } = window.require('electron'); - API_KEY = await ipcRenderer.invoke('get-stored-api-key'); - } catch (error) { - console.error('Failed to get API key via IPC:', error); - } - } - - if (!API_KEY) { - API_KEY = process.env.OPENAI_API_KEY; - } - - if (!API_KEY) { - throw new Error('No API key found in storage, IPC, or environment'); - } - - console.log('[Renderer] Using API key for message request'); - - const messages = [ - { - role: 'system', - content: systemPrompt, - }, - { - role: 'user', - content: [ - { - type: 'text', - text: `User Request: ${userPrompt.trim()}`, - }, - ], - }, - ]; - - if (screenshotBase64) { - messages[1].content.push({ - type: 'image_url', - image_url: { - url: `data:image/jpeg;base64,${screenshotBase64}`, - }, - }); - console.log('πŸ“· Screenshot included in message request'); - } - - const { isLoggedIn } = await queryLoginState(); - const provider = await ipcRenderer.invoke('get-ai-provider'); - const usePortkey = isLoggedIn && provider === 'openai'; - - console.log(`πŸš€ Sending request to ${provider} AI...`); - - const response = await makeStreamingChatCompletionWithPortkey({ - apiKey: API_KEY, - provider: provider, - messages: messages, - temperature: 0.7, - maxTokens: 2048, - model: provider === 'openai' ? 'gpt-4.1' : 'gemini-2.5-flash', - usePortkey: usePortkey, - portkeyVirtualKey: usePortkey ? API_KEY : null - }); - - // --- 슀트리밍 응닡 처리 --- - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - let fullResponse = ''; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - const chunk = decoder.decode(value); - const lines = chunk.split('\n').filter(line => line.trim() !== ''); - - for (const line of lines) { - if (line.startsWith('data: ')) { - const data = line.substring(6); - if (data === '[DONE]') { - // 슀트리밍 μ’…λ£Œ μ‹ ν˜Έ - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.send('ask-response-stream-end'); - - // Save the full conversation to DB - ipcRenderer.invoke('save-ask-message', { - userPrompt: userPrompt.trim(), - aiResponse: fullResponse - }).then(result => { - if (result.success) { - console.log('Ask/answer pair saved successfully.'); - } else { - console.error('Failed to save ask/answer pair:', result.error); - } - }); - } - return { success: true, response: fullResponse }; - } - try { - const json = JSON.parse(data); - const token = json.choices[0]?.delta?.content || ''; - if (token) { - fullResponse += token; - // πŸ’‘ λ Œλ”λŸ¬ ν”„λ‘œμ„ΈμŠ€μ— 토큰 청크 전솑 - if (window.require) { - const { ipcRenderer } = window.require('electron'); - ipcRenderer.send('ask-response-chunk', { token }); - } - } - } catch (error) { - console.error('Error parsing stream data chunk:', error, 'Chunk:', data); - } - } - } - } - // 이 뢀뢄은 슀트리밍이 λλ‚˜λ©΄ 사싀상 λ„λ‹¬ν•˜μ§€ μ•ŠμŒ - return { success: true, response: fullResponse }; - } catch (error) { - console.error('Error processing message:', error); - const errorMessage = `Error: ${error.message}`; - - return { success: false, error: error.message, response: errorMessage }; - } -} - - -const apiClient = window.require ? window.require('../common/services/apiClient') : undefined; - -async function initConversationStorage() { - try { - const isOnline = await apiClient.checkConnection(); - console.log('API μ—°κ²° μƒνƒœ:', isOnline); - return isOnline; - } catch (error) { - console.error('API μ—°κ²° μ‹€νŒ¨:', error); - return false; - } -} - -async function getConversationSession(sessionId) { - try { - if (!apiClient) { - throw new Error('API client not available'); - } - - const response = await apiClient.client.get(`/api/conversations/${sessionId}`); - return response.data; - } catch (error) { - console.error('λŒ€ν™” μ„Έμ…˜ 쑰회 μ‹€νŒ¨:', error); - throw error; - } -} - -async function getAllConversationSessions() { - try { - if (!apiClient) { - throw new Error('API client not available'); - } - - const response = await apiClient.client.get('/api/conversations'); - return response.data; - } catch (error) { - console.error('전체 λŒ€ν™” μ„Έμ…˜ 쑰회 μ‹€νŒ¨:', error); - throw error; - } -} - -// Initialize conversation storage when renderer loads -initConversationStorage().catch(console.error); - -window.pickleGlass = { - initializeopenai, - startCapture, - stopCapture, - sendMessage, - // Conversation history functions - getAllConversationSessions, - getConversationSession, - initConversationStorage, - isLinux: isLinux, - isMacOS: isMacOS, - e: pickleGlassElement, -}; - -// ------------------------------------------------------- -// πŸ”” React to session state changes from the main process -// When the session ends (isActive === false), ensure we stop -// all local capture pipelines (mic, screen, etc.). -// ------------------------------------------------------- -ipcRenderer.on('session-state-changed', (_event, { isActive }) => { - if (!isActive) { - console.log('[Renderer] Session ended – stopping local capture'); - stopCapture(); - } else { - console.log('[Renderer] New session started – clearing in-memory history and summaries'); - - // Reset live conversation & analysis caches - realtimeConversationHistory = []; - - const blankData = { - summary: [], - topic: { header: '', bullets: [] }, - actions: [], - followUps: [], - }; - window.pickleGlass.setStructuredData(blankData); - } -}); diff --git a/src/features/listen/renderer/listenCapture.js b/src/features/listen/renderer/listenCapture.js new file mode 100644 index 0000000..1607d0b --- /dev/null +++ b/src/features/listen/renderer/listenCapture.js @@ -0,0 +1,706 @@ +const { ipcRenderer } = require('electron'); +const createAecModule = require('../../../assets/aec.js'); + +let aecModPromise = null; // ν•œ 번만 λ‘œλ“œ +let aecMod = null; +let aecPtr = 0; // Rust Aec* 1개만 μž¬μ‚¬μš© + +/** WASM λͺ¨λ“ˆ κ°€μ Έμ˜€κ³  1회 μ΄ˆκΈ°ν™” */ +async function getAec () { + if (aecModPromise) return aecModPromise; // μΊμ‹œ + + aecModPromise = createAecModule().then((M) => { + aecMod = M; + // C 심볼 β†’ JS 래퍼 바인딩 (λ”± 1번) + M.newPtr = M.cwrap('AecNew', 'number', + ['number','number','number','number']); + M.cancel = M.cwrap('AecCancelEcho', null, + ['number','number','number','number','number']); + M.destroy = M.cwrap('AecDestroy', null, ['number']); + return M; + }); + + return aecModPromise; +} + +// λ°”λ‘œ λ‘œλ“œ-μ‹€νŒ¨ 둜그λ₯Ό 보기 μœ„ν•΄ +getAec().catch(console.error); +// --------------------------- +// Constants & Globals +// --------------------------- +const SAMPLE_RATE = 24000; +const AUDIO_CHUNK_DURATION = 0.1; +const BUFFER_SIZE = 4096; + +const isLinux = process.platform === 'linux'; +const isMacOS = process.platform === 'darwin'; + +let mediaStream = null; +let micMediaStream = null; +let screenshotInterval = null; +let audioContext = null; +let audioProcessor = null; +let systemAudioContext = null; +let systemAudioProcessor = null; +let currentImageQuality = 'medium'; +let lastScreenshotBase64 = null; + +let systemAudioBuffer = []; +const MAX_SYSTEM_BUFFER_SIZE = 10; + +// --------------------------- +// Utility helpers (exact from renderer.js) +// --------------------------- +function isVoiceActive(audioFloat32Array, threshold = 0.005) { + if (!audioFloat32Array || audioFloat32Array.length === 0) { + return false; + } + + let sumOfSquares = 0; + for (let i = 0; i < audioFloat32Array.length; i++) { + sumOfSquares += audioFloat32Array[i] * audioFloat32Array[i]; + } + const rms = Math.sqrt(sumOfSquares / audioFloat32Array.length); + + // console.log(`VAD RMS: ${rms.toFixed(4)}`); // For debugging VAD threshold + + return rms > threshold; +} + +function base64ToFloat32Array(base64) { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + const int16Array = new Int16Array(bytes.buffer); + const float32Array = new Float32Array(int16Array.length); + + for (let i = 0; i < int16Array.length; i++) { + float32Array[i] = int16Array[i] / 32768.0; + } + + return float32Array; +} + +function convertFloat32ToInt16(float32Array) { + const int16Array = new Int16Array(float32Array.length); + for (let i = 0; i < float32Array.length; i++) { + // Improved scaling to prevent clipping + const s = Math.max(-1, Math.min(1, float32Array[i])); + int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7fff; + } + return int16Array; +} + +function arrayBufferToBase64(buffer) { + let binary = ''; + const bytes = new Uint8Array(buffer); + const len = bytes.byteLength; + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + return btoa(binary); +} + +/* ───────────────────────── JS β†”οΈŽ WASM 헬퍼 ───────────────────────── */ +function int16PtrFromFloat32(mod, f32) { + const len = f32.length; + const bytes = len * 2; + const ptr = mod._malloc(bytes); + // HEAP16이 μ—†μœΌλ©΄ HEAPU8.buffer둜 직접 λž˜ν•‘ + const heapBuf = (mod.HEAP16 ? mod.HEAP16.buffer : mod.HEAPU8.buffer); + const i16 = new Int16Array(heapBuf, ptr, len); + for (let i = 0; i < len; ++i) { + const s = Math.max(-1, Math.min(1, f32[i])); + i16[i] = s < 0 ? s * 0x8000 : s * 0x7fff; + } + return { ptr, view: i16 }; +} + +function float32FromInt16View(i16) { + const out = new Float32Array(i16.length); + for (let i = 0; i < i16.length; ++i) out[i] = i16[i] / 32768; + return out; +} + +/* ν•„μš”ν•˜λ‹€λ©΄ μ’…λ£Œ μ‹œ */ +function disposeAec () { + getAec().then(mod => { if (aecPtr) mod.destroy(aecPtr); }); +} + +function runAecSync (micF32, sysF32) { + if (!aecMod || !aecPtr || !aecMod.HEAPU8) return micF32; // 아직 λͺ¨λ“ˆ μ•ˆ 뜸 β†’ 패슀 + + const len = micF32.length; + const mic = int16PtrFromFloat32(aecMod, micF32); + const echo = int16PtrFromFloat32(aecMod, sysF32); + const out = aecMod._malloc(len * 2); + + aecMod.cancel(aecPtr, mic.ptr, echo.ptr, out, len); + + const heapBuf = (aecMod.HEAP16 ? aecMod.HEAP16.buffer : aecMod.HEAPU8.buffer); + const outF32 = float32FromInt16View(new Int16Array(heapBuf, out, len)); + + aecMod._free(mic.ptr); aecMod._free(echo.ptr); aecMod._free(out); + return outF32; +} + + +// System audio data handler +ipcRenderer.on('system-audio-data', (event, { data }) => { + systemAudioBuffer.push({ + data: data, + timestamp: Date.now(), + }); + + // 였래된 데이터 제거 + if (systemAudioBuffer.length > MAX_SYSTEM_BUFFER_SIZE) { + systemAudioBuffer = systemAudioBuffer.slice(-MAX_SYSTEM_BUFFER_SIZE); + } +}); + +// --------------------------- +// Complete token tracker (exact from renderer.js) +// --------------------------- +let tokenTracker = { + tokens: [], + audioStartTime: null, + + addTokens(count, type = 'image') { + const now = Date.now(); + this.tokens.push({ + timestamp: now, + count: count, + type: type, + }); + + this.cleanOldTokens(); + }, + + calculateImageTokens(width, height) { + const pixels = width * height; + if (pixels <= 384 * 384) { + return 85; + } + + const tiles = Math.ceil(pixels / (768 * 768)); + return tiles * 85; + }, + + trackAudioTokens() { + if (!this.audioStartTime) { + this.audioStartTime = Date.now(); + return; + } + + const now = Date.now(); + const elapsedSeconds = (now - this.audioStartTime) / 1000; + + const audioTokens = Math.floor(elapsedSeconds * 16); + + if (audioTokens > 0) { + this.addTokens(audioTokens, 'audio'); + this.audioStartTime = now; + } + }, + + cleanOldTokens() { + const oneMinuteAgo = Date.now() - 60 * 1000; + this.tokens = this.tokens.filter(token => token.timestamp > oneMinuteAgo); + }, + + getTokensInLastMinute() { + this.cleanOldTokens(); + return this.tokens.reduce((total, token) => total + token.count, 0); + }, + + shouldThrottle() { + const throttleEnabled = localStorage.getItem('throttleTokens') === 'true'; + if (!throttleEnabled) { + return false; + } + + const maxTokensPerMin = parseInt(localStorage.getItem('maxTokensPerMin') || '500000', 10); + const throttleAtPercent = parseInt(localStorage.getItem('throttleAtPercent') || '75', 10); + + const currentTokens = this.getTokensInLastMinute(); + const throttleThreshold = Math.floor((maxTokensPerMin * throttleAtPercent) / 100); + + console.log(`Token check: ${currentTokens}/${maxTokensPerMin} (throttle at ${throttleThreshold})`); + + return currentTokens >= throttleThreshold; + }, + + // Reset the tracker + reset() { + this.tokens = []; + this.audioStartTime = null; + }, +}; + +// Track audio tokens every few seconds +setInterval(() => { + tokenTracker.trackAudioTokens(); +}, 2000); + +// --------------------------- +// Audio processing functions (exact from renderer.js) +// --------------------------- +async function setupMicProcessing(micStream) { + /* ── WASM λ¨Όμ € λ‘œλ“œ ───────────────────────── */ + const mod = await getAec(); + if (!aecPtr) aecPtr = mod.newPtr(160, 1600, 24000, 1); + + + const micAudioContext = new AudioContext({ sampleRate: SAMPLE_RATE }); + await micAudioContext.resume(); + const micSource = micAudioContext.createMediaStreamSource(micStream); + const micProcessor = micAudioContext.createScriptProcessor(BUFFER_SIZE, 1, 1); + + let audioBuffer = []; + const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION; + + micProcessor.onaudioprocess = (e) => { + const inputData = e.inputBuffer.getChannelData(0); + audioBuffer.push(...inputData); + console.log('🎀 micProcessor.onaudioprocess'); + + // samplesPerChunk(=2400) 만큼 λͺ¨μ΄λ©΄ 전솑 + while (audioBuffer.length >= samplesPerChunk) { + let chunk = audioBuffer.splice(0, samplesPerChunk); + let processedChunk = new Float32Array(chunk); // κΈ°λ³Έκ°’ + + // ───────────────── WASM AEC ───────────────── + if (systemAudioBuffer.length > 0) { + const latest = systemAudioBuffer[systemAudioBuffer.length - 1]; + const sysF32 = base64ToFloat32Array(latest.data); + + // **μŒμ„± ꡬ간일 λ•Œλ§Œ 런** + processedChunk = runAecSync(new Float32Array(chunk), sysF32); + console.log('πŸ”Š Applied WASM-AEC (speex)'); + } else { + console.log('πŸ”Š No system audio for AEC reference'); + } + + const pcm16 = convertFloat32ToInt16(processedChunk); + const b64 = arrayBufferToBase64(pcm16.buffer); + + ipcRenderer.invoke('send-audio-content', { + data: b64, + mimeType: 'audio/pcm;rate=24000', + }); + } + }; + + micSource.connect(micProcessor); + micProcessor.connect(micAudioContext.destination); + + audioProcessor = micProcessor; + return { context: micAudioContext, processor: micProcessor }; +} + +function setupLinuxMicProcessing(micStream) { + // Setup microphone audio processing for Linux + const micAudioContext = new AudioContext({ sampleRate: SAMPLE_RATE }); + const micSource = micAudioContext.createMediaStreamSource(micStream); + const micProcessor = micAudioContext.createScriptProcessor(BUFFER_SIZE, 1, 1); + + let audioBuffer = []; + const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION; + + micProcessor.onaudioprocess = async e => { + const inputData = e.inputBuffer.getChannelData(0); + audioBuffer.push(...inputData); + + // Process audio in chunks + while (audioBuffer.length >= samplesPerChunk) { + const chunk = audioBuffer.splice(0, samplesPerChunk); + const pcmData16 = convertFloat32ToInt16(chunk); + const base64Data = arrayBufferToBase64(pcmData16.buffer); + + await ipcRenderer.invoke('send-audio-content', { + data: base64Data, + mimeType: 'audio/pcm;rate=24000', + }); + } + }; + + micSource.connect(micProcessor); + micProcessor.connect(micAudioContext.destination); + + // Store processor reference for cleanup + audioProcessor = micProcessor; +} + +function setupSystemAudioProcessing(systemStream) { + const systemAudioContext = new AudioContext({ sampleRate: SAMPLE_RATE }); + const systemSource = systemAudioContext.createMediaStreamSource(systemStream); + const systemProcessor = systemAudioContext.createScriptProcessor(BUFFER_SIZE, 1, 1); + + let audioBuffer = []; + const samplesPerChunk = SAMPLE_RATE * AUDIO_CHUNK_DURATION; + + systemProcessor.onaudioprocess = async e => { + const inputData = e.inputBuffer.getChannelData(0); + if (!inputData || inputData.length === 0) return; + + audioBuffer.push(...inputData); + + while (audioBuffer.length >= samplesPerChunk) { + const chunk = audioBuffer.splice(0, samplesPerChunk); + const pcmData16 = convertFloat32ToInt16(chunk); + const base64Data = arrayBufferToBase64(pcmData16.buffer); + + try { + await ipcRenderer.invoke('send-system-audio-content', { + data: base64Data, + mimeType: 'audio/pcm;rate=24000', + }); + } catch (error) { + console.error('Failed to send system audio:', error); + } + } + }; + + systemSource.connect(systemProcessor); + systemProcessor.connect(systemAudioContext.destination); + + return { context: systemAudioContext, processor: systemProcessor }; +} + +// --------------------------- +// Screenshot functions (exact from renderer.js) +// --------------------------- +async function captureScreenshot(imageQuality = 'medium', isManual = false) { + console.log(`Capturing ${isManual ? 'manual' : 'automated'} screenshot...`); + + // Check rate limiting for automated screenshots only + if (!isManual && tokenTracker.shouldThrottle()) { + console.log('⚠️ Automated screenshot skipped due to rate limiting'); + return; + } + + try { + // Request screenshot from main process + const result = await ipcRenderer.invoke('capture-screenshot', { + quality: imageQuality, + }); + + if (result.success && result.base64) { + // Store the latest screenshot + lastScreenshotBase64 = result.base64; + + // Note: sendResult is not defined in the original, this was likely an error + // Commenting out this section as it references undefined variable + /* + if (sendResult.success) { + // Track image tokens after successful send + const imageTokens = tokenTracker.calculateImageTokens(result.width || 1920, result.height || 1080); + tokenTracker.addTokens(imageTokens, 'image'); + console.log(`πŸ“Š Image sent successfully - ${imageTokens} tokens used (${result.width}x${result.height})`); + } else { + console.error('Failed to send image:', sendResult.error); + } + */ + } else { + console.error('Failed to capture screenshot:', result.error); + } + } catch (error) { + console.error('Error capturing screenshot:', error); + } +} + +async function captureManualScreenshot(imageQuality = null) { + console.log('Manual screenshot triggered'); + const quality = imageQuality || currentImageQuality; + await captureScreenshot(quality, true); +} + +async function getCurrentScreenshot() { + try { + // First try to get a fresh screenshot from main process + const result = await ipcRenderer.invoke('get-current-screenshot'); + + if (result.success && result.base64) { + console.log('πŸ“Έ Got fresh screenshot from main process'); + return result.base64; + } + + // If no screenshot available, capture one now + console.log('πŸ“Έ No screenshot available, capturing new one'); + const captureResult = await ipcRenderer.invoke('capture-screenshot', { + quality: currentImageQuality, + }); + + if (captureResult.success && captureResult.base64) { + lastScreenshotBase64 = captureResult.base64; + return captureResult.base64; + } + + // Fallback to last stored screenshot + if (lastScreenshotBase64) { + console.log('πŸ“Έ Using cached screenshot'); + return lastScreenshotBase64; + } + + throw new Error('Failed to get screenshot'); + } catch (error) { + console.error('Error getting current screenshot:', error); + return null; + } +} + +// --------------------------- +// Main capture functions (exact from renderer.js) +// --------------------------- +async function startCapture(screenshotIntervalSeconds = 5, imageQuality = 'medium') { + // Store the image quality for manual screenshots + currentImageQuality = imageQuality; + + // Reset token tracker when starting new capture session + tokenTracker.reset(); + console.log('🎯 Token tracker reset for new capture session'); + + try { + if (isMacOS) { + // On macOS, use SystemAudioDump for audio and getDisplayMedia for screen + console.log('Starting macOS capture with SystemAudioDump...'); + + // Start macOS audio capture + const audioResult = await ipcRenderer.invoke('start-macos-audio'); + if (!audioResult.success) { + console.warn('[listenCapture] macOS audio start failed:', audioResult.error); + + // 이미 μ‹€ν–‰ 쀑 β†’ stop ν›„ μž¬μ‹œλ„ + if (audioResult.error === 'already_running') { + await ipcRenderer.invoke('stop-macos-audio'); + await new Promise(r => setTimeout(r, 500)); + const retry = await ipcRenderer.invoke('start-macos-audio'); + if (!retry.success) { + throw new Error('Retry failed: ' + retry.error); + } + } else { + throw new Error('Failed to start macOS audio capture: ' + audioResult.error); + } + } + + // Initialize screen capture in main process + const screenResult = await ipcRenderer.invoke('start-screen-capture'); + if (!screenResult.success) { + throw new Error('Failed to start screen capture: ' + screenResult.error); + } + + + try { + micMediaStream = await navigator.mediaDevices.getUserMedia({ + audio: { + sampleRate: SAMPLE_RATE, + channelCount: 1, + echoCancellation: true, + noiseSuppression: true, + autoGainControl: true, + }, + video: false, + }); + + console.log('macOS microphone capture started'); + const { context, processor } = await setupMicProcessing(micMediaStream); + audioContext = context; + audioProcessor = processor; + } catch (micErr) { + console.warn('Failed to get microphone on macOS:', micErr); + } + ////////// for index & subjects ////////// + + console.log('macOS screen capture started - audio handled by SystemAudioDump'); + } else if (isLinux) { + // Linux - use display media for screen capture and getUserMedia for microphone + mediaStream = await navigator.mediaDevices.getDisplayMedia({ + video: { + frameRate: 1, + width: { ideal: 1920 }, + height: { ideal: 1080 }, + }, + audio: false, // Don't use system audio loopback on Linux + }); + + // Get microphone input for Linux + let micMediaStream = null; + try { + micMediaStream = await navigator.mediaDevices.getUserMedia({ + audio: { + sampleRate: SAMPLE_RATE, + channelCount: 1, + echoCancellation: true, + noiseSuppression: true, + autoGainControl: true, + }, + video: false, + }); + + console.log('Linux microphone capture started'); + + // Setup audio processing for microphone on Linux + setupLinuxMicProcessing(micMediaStream); + } catch (micError) { + console.warn('Failed to get microphone access on Linux:', micError); + // Continue without microphone if permission denied + } + + console.log('Linux screen capture started'); + } else { + // Windows - capture mic and system audio separately using native loopback + console.log('Starting Windows capture with native loopback audio...'); + + // Start screen capture in main process for screenshots + const screenResult = await ipcRenderer.invoke('start-screen-capture'); + if (!screenResult.success) { + throw new Error('Failed to start screen capture: ' + screenResult.error); + } + + // Ensure STT sessions are initialized before starting audio capture + const sessionActive = await ipcRenderer.invoke('is-session-active'); + if (!sessionActive) { + throw new Error('STT sessions not initialized - please wait for initialization to complete'); + } + + // 1. Get user's microphone + try { + micMediaStream = await navigator.mediaDevices.getUserMedia({ + audio: { + sampleRate: SAMPLE_RATE, + channelCount: 1, + echoCancellation: true, + noiseSuppression: true, + autoGainControl: true, + }, + video: false, + }); + console.log('Windows microphone capture started'); + const { context, processor } = await setupMicProcessing(micMediaStream); + audioContext = context; + audioProcessor = processor; + } catch (micErr) { + console.warn('Could not get microphone access on Windows:', micErr); + } + + // 2. Get system audio using native Electron loopback + try { + mediaStream = await navigator.mediaDevices.getDisplayMedia({ + video: true, + audio: true // This will now use native loopback from our handler + }); + + // Verify we got audio tracks + const audioTracks = mediaStream.getAudioTracks(); + if (audioTracks.length === 0) { + throw new Error('No audio track in native loopback stream'); + } + + console.log('Windows native loopback audio capture started'); + const { context, processor } = setupSystemAudioProcessing(mediaStream); + systemAudioContext = context; + systemAudioProcessor = processor; + } catch (sysAudioErr) { + console.error('Failed to start Windows native loopback audio:', sysAudioErr); + // Continue without system audio + } + } + + // Start capturing screenshots - check if manual mode + if (screenshotIntervalSeconds === 'manual' || screenshotIntervalSeconds === 'Manual') { + console.log('Manual mode enabled - screenshots will be captured on demand only'); + // Don't start automatic capture in manual mode + } else { + // μŠ€ν¬λ¦°μƒ· κΈ°λŠ₯ ν™œμ„±ν™” (chatModelμ—μ„œ μ‚¬μš©) + const intervalMilliseconds = parseInt(screenshotIntervalSeconds) * 1000; + screenshotInterval = setInterval(() => captureScreenshot(imageQuality), intervalMilliseconds); + + // Capture first screenshot immediately + setTimeout(() => captureScreenshot(imageQuality), 100); + console.log(`πŸ“Έ Screenshot capture enabled with ${screenshotIntervalSeconds}s interval`); + } + } catch (err) { + console.error('Error starting capture:', err); + // Note: pickleGlass.e() is not available in this context, commenting out + // pickleGlass.e().setStatus('error'); + } +} + +function stopCapture() { + if (screenshotInterval) { + clearInterval(screenshotInterval); + screenshotInterval = null; + } + + // Clean up microphone resources + if (audioProcessor) { + audioProcessor.disconnect(); + audioProcessor = null; + } + if (audioContext) { + audioContext.close(); + audioContext = null; + } + + // Clean up system audio resources + if (systemAudioProcessor) { + systemAudioProcessor.disconnect(); + systemAudioProcessor = null; + } + if (systemAudioContext) { + systemAudioContext.close(); + systemAudioContext = null; + } + + // Stop and release media stream tracks + if (mediaStream) { + mediaStream.getTracks().forEach(track => track.stop()); + mediaStream = null; + } + if (micMediaStream) { + micMediaStream.getTracks().forEach(t => t.stop()); + micMediaStream = null; + } + + // Stop screen capture in main process + ipcRenderer.invoke('stop-screen-capture').catch(err => { + console.error('Error stopping screen capture:', err); + }); + + // Stop macOS audio capture if running + if (isMacOS) { + ipcRenderer.invoke('stop-macos-audio').catch(err => { + console.error('Error stopping macOS audio:', err); + }); + } +} + +// --------------------------- +// Exports & global registration +// --------------------------- +module.exports = { + getAec, // μƒˆλ‘œ λ§Œλ“  μ΄ˆκΈ°ν™” ν•¨μˆ˜ + runAecSync, // sync 버전 + disposeAec, // ν•„μš”μ‹œ Rust 객체 파괴 + startCapture, + stopCapture, + captureManualScreenshot, + getCurrentScreenshot, + isLinux, + isMacOS, +}; + +// Expose functions to global scope for external access (exact from renderer.js) +if (typeof window !== 'undefined') { + window.captureManualScreenshot = captureManualScreenshot; + window.listenCapture = module.exports; + window.pickleGlass = window.pickleGlass || {}; + window.pickleGlass.startCapture = startCapture; + window.pickleGlass.stopCapture = stopCapture; + window.pickleGlass.captureManualScreenshot = captureManualScreenshot; + window.pickleGlass.getCurrentScreenshot = getCurrentScreenshot; +} \ No newline at end of file diff --git a/src/features/listen/renderer/renderer.js b/src/features/listen/renderer/renderer.js new file mode 100644 index 0000000..6601b48 --- /dev/null +++ b/src/features/listen/renderer/renderer.js @@ -0,0 +1,138 @@ +// renderer.js +const { ipcRenderer } = require('electron'); +const listenCapture = require('./listenCapture.js'); + +let realtimeConversationHistory = []; + +async function queryLoginState() { + const userState = await ipcRenderer.invoke('get-current-user'); + return userState; +} + +function pickleGlassElement() { + return document.getElementById('pickle-glass'); +} + +async function initializeopenai(profile = 'interview', language = 'en') { + // The API key is now handled in the main process from .env file. + // We just need to trigger the initialization. + try { + console.log(`Requesting OpenAI initialization with profile: ${profile}, language: ${language}`); + const success = await ipcRenderer.invoke('initialize-openai', profile, language); + if (success) { + // The status will be updated via 'update-status' event from the main process. + console.log('OpenAI initialization successful.'); + } else { + console.error('OpenAI initialization failed.'); + const appElement = pickleGlassElement(); + if (appElement && typeof appElement.setStatus === 'function') { + appElement.setStatus('Initialization Failed'); + } + } + } catch (error) { + console.error('Error during OpenAI initialization IPC call:', error); + const appElement = pickleGlassElement(); + if (appElement && typeof appElement.setStatus === 'function') { + appElement.setStatus('Error'); + } + } +} + +// Listen for status updates +ipcRenderer.on('update-status', (event, status) => { + console.log('Status update:', status); + pickleGlass.e().setStatus(status); +}); + +// Listen for real-time STT updates +ipcRenderer.on('stt-update', (event, data) => { + console.log('Renderer.js stt-update', data); + const { speaker, text, isFinal, isPartial, timestamp } = data; + + if (isPartial) { + console.log(`πŸ”„ [${speaker} - partial]: ${text}`); + } else if (isFinal) { + console.log(`βœ… [${speaker} - final]: ${text}`); + + const speakerText = speaker.toLowerCase(); + const conversationText = `${speakerText}: ${text.trim()}`; + + realtimeConversationHistory.push(conversationText); + + if (realtimeConversationHistory.length > 30) { + realtimeConversationHistory = realtimeConversationHistory.slice(-30); + } + + console.log(`πŸ“ Updated realtime conversation history: ${realtimeConversationHistory.length} texts`); + console.log(`πŸ“‹ Latest text: ${conversationText}`); + } + + if (pickleGlass.e() && typeof pickleGlass.e().updateRealtimeTranscription === 'function') { + pickleGlass.e().updateRealtimeTranscription({ + speaker, + text, + isFinal, + isPartial, + timestamp, + }); + } +}); + +ipcRenderer.on('update-structured-data', (_, structuredData) => { + console.log('πŸ“₯ Received structured data update:', structuredData); + window.pickleGlass.structuredData = structuredData; + window.pickleGlass.setStructuredData(structuredData); +}); + +window.pickleGlass.structuredData = { + summary: [], + topic: { header: '', bullets: [] }, + actions: [], +}; + +window.pickleGlass.setStructuredData = data => { + window.pickleGlass.structuredData = data; + pickleGlass.e()?.updateStructuredData?.(data); +}; + +function formatRealtimeConversationHistory() { + if (realtimeConversationHistory.length === 0) return 'No conversation history available.'; + + return realtimeConversationHistory.slice(-30).join('\n'); +} + +window.pickleGlass = { + initializeopenai, + startCapture: listenCapture.startCapture, + stopCapture: listenCapture.stopCapture, + isLinux: listenCapture.isLinux, + isMacOS: listenCapture.isMacOS, + captureManualScreenshot: listenCapture.captureManualScreenshot, + getCurrentScreenshot: listenCapture.getCurrentScreenshot, + e: pickleGlassElement, +}; + +// ------------------------------------------------------- +// πŸ”” React to session state changes from the main process +// When the session ends (isActive === false), ensure we stop +// all local capture pipelines (mic, screen, etc.). +// ------------------------------------------------------- +ipcRenderer.on('session-state-changed', (_event, { isActive }) => { + if (!isActive) { + console.log('[Renderer] Session ended – stopping local capture'); + listenCapture.stopCapture(); + } else { + console.log('[Renderer] New session started – clearing in-memory history and summaries'); + + // Reset live conversation & analysis caches + realtimeConversationHistory = []; + + const blankData = { + summary: [], + topic: { header: '', bullets: [] }, + actions: [], + followUps: [], + }; + window.pickleGlass.setStructuredData(blankData); + } +}); diff --git a/src/features/listen/stt/SttView.js b/src/features/listen/stt/SttView.js new file mode 100644 index 0000000..31e2a5a --- /dev/null +++ b/src/features/listen/stt/SttView.js @@ -0,0 +1,228 @@ +import { html, css, LitElement } from '../../../assets/lit-core-2.7.4.min.js'; + +export class SttView extends LitElement { + static styles = css` + :host { + display: block; + width: 100%; + } + + /* Inherit font styles from parent */ + + .transcription-container { + overflow-y: auto; + padding: 12px 12px 16px 12px; + display: flex; + flex-direction: column; + gap: 8px; + min-height: 150px; + max-height: 600px; + position: relative; + z-index: 1; + flex: 1; + } + + /* Visibility handled by parent component */ + + .transcription-container::-webkit-scrollbar { + width: 8px; + } + .transcription-container::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.1); + border-radius: 4px; + } + .transcription-container::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3); + border-radius: 4px; + } + .transcription-container::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.5); + } + + .stt-message { + padding: 8px 12px; + border-radius: 12px; + max-width: 80%; + word-wrap: break-word; + word-break: break-word; + line-height: 1.5; + font-size: 13px; + margin-bottom: 4px; + box-sizing: border-box; + } + + .stt-message.them { + background: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.9); + align-self: flex-start; + border-bottom-left-radius: 4px; + margin-right: auto; + } + + .stt-message.me { + background: rgba(0, 122, 255, 0.8); + color: white; + align-self: flex-end; + border-bottom-right-radius: 4px; + margin-left: auto; + } + + .empty-state { + display: flex; + align-items: center; + justify-content: center; + height: 100px; + color: rgba(255, 255, 255, 0.6); + font-size: 12px; + font-style: italic; + } + `; + + static properties = { + sttMessages: { type: Array }, + isVisible: { type: Boolean }, + }; + + constructor() { + super(); + this.sttMessages = []; + this.isVisible = true; + this.messageIdCounter = 0; + this._shouldScrollAfterUpdate = false; + + this.handleSttUpdate = this.handleSttUpdate.bind(this); + } + + connectedCallback() { + super.connectedCallback(); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.on('stt-update', this.handleSttUpdate); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.removeListener('stt-update', this.handleSttUpdate); + } + } + + // Handle session reset from parent + resetTranscript() { + this.sttMessages = []; + this.requestUpdate(); + } + + handleSttUpdate(event, { speaker, text, isFinal, isPartial }) { + if (text === undefined) return; + + const container = this.shadowRoot.querySelector('.transcription-container'); + this._shouldScrollAfterUpdate = container ? container.scrollTop + container.clientHeight >= container.scrollHeight - 10 : false; + + const findLastPartialIdx = spk => { + for (let i = this.sttMessages.length - 1; i >= 0; i--) { + const m = this.sttMessages[i]; + if (m.speaker === spk && m.isPartial) return i; + } + return -1; + }; + + const newMessages = [...this.sttMessages]; + const targetIdx = findLastPartialIdx(speaker); + + if (isPartial) { + if (targetIdx !== -1) { + newMessages[targetIdx] = { + ...newMessages[targetIdx], + text, + isPartial: true, + isFinal: false, + }; + } else { + newMessages.push({ + id: this.messageIdCounter++, + speaker, + text, + isPartial: true, + isFinal: false, + }); + } + } else if (isFinal) { + if (targetIdx !== -1) { + newMessages[targetIdx] = { + ...newMessages[targetIdx], + text, + isPartial: false, + isFinal: true, + }; + } else { + newMessages.push({ + id: this.messageIdCounter++, + speaker, + text, + isPartial: false, + isFinal: true, + }); + } + } + + this.sttMessages = newMessages; + + // Notify parent component about message updates + this.dispatchEvent(new CustomEvent('stt-messages-updated', { + detail: { messages: this.sttMessages }, + bubbles: true + })); + } + + scrollToBottom() { + setTimeout(() => { + const container = this.shadowRoot.querySelector('.transcription-container'); + if (container) { + container.scrollTop = container.scrollHeight; + } + }, 0); + } + + getSpeakerClass(speaker) { + return speaker.toLowerCase() === 'me' ? 'me' : 'them'; + } + + getTranscriptText() { + return this.sttMessages.map(msg => `${msg.speaker}: ${msg.text}`).join('\n'); + } + + updated(changedProperties) { + super.updated(changedProperties); + + if (changedProperties.has('sttMessages')) { + if (this._shouldScrollAfterUpdate) { + this.scrollToBottom(); + this._shouldScrollAfterUpdate = false; + } + } + } + + render() { + if (!this.isVisible) { + return html`
`; + } + + return html` +
+ ${this.sttMessages.length === 0 + ? html`
Waiting for speech...
` + : this.sttMessages.map(msg => html` +
+ ${msg.text} +
+ `) + } +
+ `; + } +} + +customElements.define('stt-view', SttView); \ No newline at end of file diff --git a/src/features/listen/stt/repositories/index.js b/src/features/listen/stt/repositories/index.js new file mode 100644 index 0000000..6de1a98 --- /dev/null +++ b/src/features/listen/stt/repositories/index.js @@ -0,0 +1,5 @@ +const sttRepository = require('./sqlite.repository'); + +module.exports = { + ...sttRepository, +}; \ No newline at end of file diff --git a/src/features/listen/stt/repositories/sqlite.repository.js b/src/features/listen/stt/repositories/sqlite.repository.js new file mode 100644 index 0000000..80c838f --- /dev/null +++ b/src/features/listen/stt/repositories/sqlite.repository.js @@ -0,0 +1,27 @@ +const sqliteClient = require('../../../../common/services/sqliteClient'); + +function addTranscript({ sessionId, speaker, text }) { + const db = sqliteClient.getDb(); + const transcriptId = require('crypto').randomUUID(); + const now = Math.floor(Date.now() / 1000); + const query = `INSERT INTO transcripts (id, session_id, start_at, speaker, text, created_at) VALUES (?, ?, ?, ?, ?, ?)`; + + try { + db.prepare(query).run(transcriptId, sessionId, now, speaker, text, now); + return { id: transcriptId }; + } catch (err) { + console.error('Error adding transcript:', err); + throw err; + } +} + +function getAllTranscriptsBySessionId(sessionId) { + const db = sqliteClient.getDb(); + const query = "SELECT * FROM transcripts WHERE session_id = ? ORDER BY start_at ASC"; + return db.prepare(query).all(sessionId); +} + +module.exports = { + addTranscript, + getAllTranscriptsBySessionId, +}; \ No newline at end of file diff --git a/src/features/listen/stt/sttService.js b/src/features/listen/stt/sttService.js new file mode 100644 index 0000000..187f76e --- /dev/null +++ b/src/features/listen/stt/sttService.js @@ -0,0 +1,526 @@ +const { BrowserWindow } = require('electron'); +const { spawn } = require('child_process'); +const { createSTT } = require('../../../common/ai/factory'); +const { getStoredApiKey, getStoredProvider, getCurrentModelInfo } = require('../../../electron/windowManager'); + +const COMPLETION_DEBOUNCE_MS = 2000; + +class SttService { + constructor() { + this.mySttSession = null; + this.theirSttSession = null; + this.myCurrentUtterance = ''; + this.theirCurrentUtterance = ''; + + // Turn-completion debouncing + this.myCompletionBuffer = ''; + this.theirCompletionBuffer = ''; + this.myCompletionTimer = null; + this.theirCompletionTimer = null; + + // System audio capture + this.systemAudioProc = null; + + // Callbacks + this.onTranscriptionComplete = null; + this.onStatusUpdate = null; + + this.modelInfo = null; + } + + setCallbacks({ onTranscriptionComplete, onStatusUpdate }) { + this.onTranscriptionComplete = onTranscriptionComplete; + this.onStatusUpdate = onStatusUpdate; + } + + sendToRenderer(channel, data) { + BrowserWindow.getAllWindows().forEach(win => { + if (!win.isDestroyed()) { + win.webContents.send(channel, data); + } + }); + } + + flushMyCompletion() { + const finalText = (this.myCompletionBuffer + this.myCurrentUtterance).trim(); + if (!this.modelInfo || !finalText) return; + + // Notify completion callback + if (this.onTranscriptionComplete) { + this.onTranscriptionComplete('Me', finalText); + } + + // Send to renderer as final + this.sendToRenderer('stt-update', { + speaker: 'Me', + text: finalText, + isPartial: false, + isFinal: true, + timestamp: Date.now(), + }); + + this.myCompletionBuffer = ''; + this.myCompletionTimer = null; + this.myCurrentUtterance = ''; + + if (this.onStatusUpdate) { + this.onStatusUpdate('Listening...'); + } + } + + flushTheirCompletion() { + const finalText = (this.theirCompletionBuffer + this.theirCurrentUtterance).trim(); + if (!this.modelInfo || !finalText) return; + + // Notify completion callback + if (this.onTranscriptionComplete) { + this.onTranscriptionComplete('Them', finalText); + } + + // Send to renderer as final + this.sendToRenderer('stt-update', { + speaker: 'Them', + text: finalText, + isPartial: false, + isFinal: true, + timestamp: Date.now(), + }); + + this.theirCompletionBuffer = ''; + this.theirCompletionTimer = null; + this.theirCurrentUtterance = ''; + + if (this.onStatusUpdate) { + this.onStatusUpdate('Listening...'); + } + } + + debounceMyCompletion(text) { + if (this.modelInfo?.provider === 'gemini') { + this.myCompletionBuffer += text; + } else { + this.myCompletionBuffer += (this.myCompletionBuffer ? ' ' : '') + text; + } + + if (this.myCompletionTimer) clearTimeout(this.myCompletionTimer); + this.myCompletionTimer = setTimeout(() => this.flushMyCompletion(), COMPLETION_DEBOUNCE_MS); + } + + debounceTheirCompletion(text) { + if (this.modelInfo?.provider === 'gemini') { + this.theirCompletionBuffer += text; + } else { + this.theirCompletionBuffer += (this.theirCompletionBuffer ? ' ' : '') + text; + } + + if (this.theirCompletionTimer) clearTimeout(this.theirCompletionTimer); + this.theirCompletionTimer = setTimeout(() => this.flushTheirCompletion(), COMPLETION_DEBOUNCE_MS); + } + + async initializeSttSessions(language = 'en') { + const effectiveLanguage = process.env.OPENAI_TRANSCRIBE_LANG || language || 'en'; + + const modelInfo = await getCurrentModelInfo(null, { type: 'stt' }); + if (!modelInfo || !modelInfo.apiKey) { + throw new Error('AI model or API key is not configured.'); + } + this.modelInfo = modelInfo; + console.log(`[SttService] Initializing STT for ${modelInfo.provider} using model ${modelInfo.model}`); + + const handleMyMessage = message => { + if (!this.modelInfo) { + console.log('[SttService] Ignoring message - session already closed'); + return; + } + + if (this.modelInfo.provider === 'gemini') { + if (!message.serverContent?.modelTurn) { + console.log('[Gemini STT - Me]', JSON.stringify(message, null, 2)); + } + + if (message.serverContent?.turnComplete) { + if (this.myCompletionTimer) { + clearTimeout(this.myCompletionTimer); + this.flushMyCompletion(); + } + return; + } + + const transcription = message.serverContent?.inputTranscription; + if (!transcription || !transcription.text) return; + + const textChunk = transcription.text; + if (!textChunk.trim() || textChunk.trim() === '') { + return; // 1. Ignore whitespace-only chunks or noise + } + + this.debounceMyCompletion(textChunk); + + this.sendToRenderer('stt-update', { + speaker: 'Me', + text: this.myCompletionBuffer, + isPartial: true, + isFinal: false, + timestamp: Date.now(), + }); + } else { + const type = message.type; + const text = message.transcript || message.delta || (message.alternatives && message.alternatives[0]?.transcript) || ''; + + if (type === 'conversation.item.input_audio_transcription.delta') { + if (this.myCompletionTimer) clearTimeout(this.myCompletionTimer); + this.myCompletionTimer = null; + this.myCurrentUtterance += text; + const continuousText = this.myCompletionBuffer + (this.myCompletionBuffer ? ' ' : '') + this.myCurrentUtterance; + if (text && !text.includes('vq_lbr_audio_')) { + this.sendToRenderer('stt-update', { + speaker: 'Me', + text: continuousText, + isPartial: true, + isFinal: false, + timestamp: Date.now(), + }); + } + } else if (type === 'conversation.item.input_audio_transcription.completed') { + if (text && text.trim()) { + const finalUtteranceText = text.trim(); + this.myCurrentUtterance = ''; + this.debounceMyCompletion(finalUtteranceText); + } + } + } + + if (message.error) { + console.error('[Me] STT Session Error:', message.error); + } + }; + + const handleTheirMessage = message => { + if (!message || typeof message !== 'object') return; + + if (!this.modelInfo) { + console.log('[SttService] Ignoring message - session already closed'); + return; + } + + if (this.modelInfo.provider === 'gemini') { + if (!message.serverContent?.modelTurn) { + console.log('[Gemini STT - Them]', JSON.stringify(message, null, 2)); + } + + if (message.serverContent?.turnComplete) { + if (this.theirCompletionTimer) { + clearTimeout(this.theirCompletionTimer); + this.flushTheirCompletion(); + } + return; + } + + const transcription = message.serverContent?.inputTranscription; + if (!transcription || !transcription.text) return; + + const textChunk = transcription.text; + if (!textChunk.trim() || textChunk.trim() === '') { + return; // 1. Ignore whitespace-only chunks or noise + } + + this.debounceTheirCompletion(textChunk); + + this.sendToRenderer('stt-update', { + speaker: 'Them', + text: this.theirCompletionBuffer, + isPartial: true, + isFinal: false, + timestamp: Date.now(), + }); + } else { + const type = message.type; + const text = message.transcript || message.delta || (message.alternatives && message.alternatives[0]?.transcript) || ''; + if (type === 'conversation.item.input_audio_transcription.delta') { + if (this.theirCompletionTimer) clearTimeout(this.theirCompletionTimer); + this.theirCompletionTimer = null; + this.theirCurrentUtterance += text; + const continuousText = this.theirCompletionBuffer + (this.theirCompletionBuffer ? ' ' : '') + this.theirCurrentUtterance; + if (text && !text.includes('vq_lbr_audio_')) { + this.sendToRenderer('stt-update', { + speaker: 'Them', + text: continuousText, + isPartial: true, + isFinal: false, + timestamp: Date.now(), + }); + } + } else if (type === 'conversation.item.input_audio_transcription.completed') { + if (text && text.trim()) { + const finalUtteranceText = text.trim(); + this.theirCurrentUtterance = ''; + this.debounceTheirCompletion(finalUtteranceText); + } + } + } + + if (message.error) { + console.error('[Them] STT Session Error:', message.error); + } + }; + + const mySttConfig = { + language: effectiveLanguage, + callbacks: { + onmessage: handleMyMessage, + onerror: error => console.error('My STT session error:', error.message), + onclose: event => console.log('My STT session closed:', event.reason), + }, + }; + + const theirSttConfig = { + language: effectiveLanguage, + callbacks: { + onmessage: handleTheirMessage, + onerror: error => console.error('Their STT session error:', error.message), + onclose: event => console.log('Their STT session closed:', event.reason), + }, + }; + + // Determine auth options for providers that support it + // const authService = require('../../../common/services/authService'); + // const userState = authService.getCurrentUser(); + // const loggedIn = userState.isLoggedIn; + + const sttOptions = { + apiKey: this.modelInfo.apiKey, + language: effectiveLanguage, + usePortkey: this.modelInfo.provider === 'openai-glass', + portkeyVirtualKey: this.modelInfo.provider === 'openai-glass' ? this.modelInfo.apiKey : undefined, + }; + + [this.mySttSession, this.theirSttSession] = await Promise.all([ + createSTT(this.modelInfo.provider, { ...sttOptions, callbacks: mySttConfig.callbacks }), + createSTT(this.modelInfo.provider, { ...sttOptions, callbacks: theirSttConfig.callbacks }), + ]); + + console.log('βœ… Both STT sessions initialized successfully.'); + return true; + } + + async sendAudioContent(data, mimeType) { + // const provider = await this.getAiProvider(); + // const isGemini = provider === 'gemini'; + + if (!this.mySttSession) { + throw new Error('User STT session not active'); + } + + let modelInfo = this.modelInfo; + if (!modelInfo) { + console.warn('[SttService] modelInfo not found, fetching on-the-fly as a fallback...'); + modelInfo = await getCurrentModelInfo(null, { type: 'stt' }); + } + if (!modelInfo) { + throw new Error('STT model info could not be retrieved.'); + } + + const payload = modelInfo.provider === 'gemini' + ? { audio: { data, mimeType: mimeType || 'audio/pcm;rate=24000' } } + : data; + + await this.mySttSession.sendRealtimeInput(payload); + } + + async sendSystemAudioContent(data, mimeType) { + if (!this.theirSttSession) { + throw new Error('Their STT session not active'); + } + + let modelInfo = this.modelInfo; + if (!modelInfo) { + console.warn('[SttService] modelInfo not found, fetching on-the-fly as a fallback...'); + modelInfo = await getCurrentModelInfo(null, { type: 'stt' }); + } + if (!modelInfo) { + throw new Error('STT model info could not be retrieved.'); + } + + const payload = modelInfo.provider === 'gemini' + ? { audio: { data, mimeType: mimeType || 'audio/pcm;rate=24000' } } + : data; + + await this.theirSttSession.sendRealtimeInput(payload); + } + + killExistingSystemAudioDump() { + return new Promise(resolve => { + console.log('Checking for existing SystemAudioDump processes...'); + + const killProc = spawn('pkill', ['-f', 'SystemAudioDump'], { + stdio: 'ignore', + }); + + killProc.on('close', code => { + if (code === 0) { + console.log('Killed existing SystemAudioDump processes'); + } else { + console.log('No existing SystemAudioDump processes found'); + } + resolve(); + }); + + killProc.on('error', err => { + console.log('Error checking for existing processes (this is normal):', err.message); + resolve(); + }); + + setTimeout(() => { + killProc.kill(); + resolve(); + }, 2000); + }); + } + + async startMacOSAudioCapture() { + if (process.platform !== 'darwin' || !this.theirSttSession) return false; + + await this.killExistingSystemAudioDump(); + console.log('Starting macOS audio capture for "Them"...'); + + const { app } = require('electron'); + const path = require('path'); + const systemAudioPath = app.isPackaged + ? path.join(process.resourcesPath, 'app.asar.unpacked', 'src', 'assets', 'SystemAudioDump') + : path.join(app.getAppPath(), 'src', 'assets', 'SystemAudioDump'); + + console.log('SystemAudioDump path:', systemAudioPath); + + this.systemAudioProc = spawn(systemAudioPath, [], { + stdio: ['ignore', 'pipe', 'pipe'], + }); + + if (!this.systemAudioProc.pid) { + console.error('Failed to start SystemAudioDump'); + return false; + } + + console.log('SystemAudioDump started with PID:', this.systemAudioProc.pid); + + const CHUNK_DURATION = 0.1; + const SAMPLE_RATE = 24000; + const BYTES_PER_SAMPLE = 2; + const CHANNELS = 2; + const CHUNK_SIZE = SAMPLE_RATE * BYTES_PER_SAMPLE * CHANNELS * CHUNK_DURATION; + + let audioBuffer = Buffer.alloc(0); + + // const provider = await this.getAiProvider(); + // const isGemini = provider === 'gemini'; + + let modelInfo = this.modelInfo; + if (!modelInfo) { + console.warn('[SttService] modelInfo not found, fetching on-the-fly as a fallback...'); + modelInfo = await getCurrentModelInfo(null, { type: 'stt' }); + } + if (!modelInfo) { + throw new Error('STT model info could not be retrieved.'); + } + + this.systemAudioProc.stdout.on('data', async data => { + audioBuffer = Buffer.concat([audioBuffer, data]); + + while (audioBuffer.length >= CHUNK_SIZE) { + const chunk = audioBuffer.slice(0, CHUNK_SIZE); + audioBuffer = audioBuffer.slice(CHUNK_SIZE); + + const monoChunk = CHANNELS === 2 ? this.convertStereoToMono(chunk) : chunk; + const base64Data = monoChunk.toString('base64'); + + this.sendToRenderer('system-audio-data', { data: base64Data }); + + if (this.theirSttSession) { + try { + const payload = modelInfo.provider === 'gemini' + ? { audio: { data: base64Data, mimeType: 'audio/pcm;rate=24000' } } + : base64Data; + await this.theirSttSession.sendRealtimeInput(payload); + } catch (err) { + console.error('Error sending system audio:', err.message); + } + } + } + }); + + this.systemAudioProc.stderr.on('data', data => { + console.error('SystemAudioDump stderr:', data.toString()); + }); + + this.systemAudioProc.on('close', code => { + console.log('SystemAudioDump process closed with code:', code); + this.systemAudioProc = null; + }); + + this.systemAudioProc.on('error', err => { + console.error('SystemAudioDump process error:', err); + this.systemAudioProc = null; + }); + + return true; + } + + convertStereoToMono(stereoBuffer) { + const samples = stereoBuffer.length / 4; + const monoBuffer = Buffer.alloc(samples * 2); + + for (let i = 0; i < samples; i++) { + const leftSample = stereoBuffer.readInt16LE(i * 4); + monoBuffer.writeInt16LE(leftSample, i * 2); + } + + return monoBuffer; + } + + stopMacOSAudioCapture() { + if (this.systemAudioProc) { + console.log('Stopping SystemAudioDump...'); + this.systemAudioProc.kill('SIGTERM'); + this.systemAudioProc = null; + } + } + + isSessionActive() { + return !!this.mySttSession && !!this.theirSttSession; + } + + async closeSessions() { + this.stopMacOSAudioCapture(); + + // Clear timers + if (this.myCompletionTimer) { + clearTimeout(this.myCompletionTimer); + this.myCompletionTimer = null; + } + if (this.theirCompletionTimer) { + clearTimeout(this.theirCompletionTimer); + this.theirCompletionTimer = null; + } + + const closePromises = []; + if (this.mySttSession) { + closePromises.push(this.mySttSession.close()); + this.mySttSession = null; + } + if (this.theirSttSession) { + closePromises.push(this.theirSttSession.close()); + this.theirSttSession = null; + } + + await Promise.all(closePromises); + console.log('All STT sessions closed.'); + + // Reset state + this.myCurrentUtterance = ''; + this.theirCurrentUtterance = ''; + this.myCompletionBuffer = ''; + this.theirCompletionBuffer = ''; + this.modelInfo = null; + } +} + +module.exports = SttService; \ No newline at end of file diff --git a/src/features/listen/summary/SummaryView.js b/src/features/listen/summary/SummaryView.js new file mode 100644 index 0000000..15d79b9 --- /dev/null +++ b/src/features/listen/summary/SummaryView.js @@ -0,0 +1,559 @@ +import { html, css, LitElement } from '../../../assets/lit-core-2.7.4.min.js'; + +export class SummaryView extends LitElement { + static styles = css` + :host { + display: block; + width: 100%; + } + + /* Inherit font styles from parent */ + + /* highlight.js μŠ€νƒ€μΌ μΆ”κ°€ */ + .insights-container pre { + background: rgba(0, 0, 0, 0.4) !important; + border-radius: 8px !important; + padding: 12px !important; + margin: 8px 0 !important; + overflow-x: auto !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; + white-space: pre !important; + word-wrap: normal !important; + word-break: normal !important; + } + + .insights-container code { + font-family: 'Monaco', 'Menlo', 'Consolas', monospace !important; + font-size: 11px !important; + background: transparent !important; + white-space: pre !important; + word-wrap: normal !important; + word-break: normal !important; + } + + .insights-container pre code { + white-space: pre !important; + word-wrap: normal !important; + word-break: normal !important; + display: block !important; + } + + .insights-container p code { + background: rgba(255, 255, 255, 0.1) !important; + padding: 2px 4px !important; + border-radius: 3px !important; + color: #ffd700 !important; + } + + .hljs-keyword { + color: #ff79c6 !important; + } + .hljs-string { + color: #f1fa8c !important; + } + .hljs-comment { + color: #6272a4 !important; + } + .hljs-number { + color: #bd93f9 !important; + } + .hljs-function { + color: #50fa7b !important; + } + .hljs-variable { + color: #8be9fd !important; + } + .hljs-built_in { + color: #ffb86c !important; + } + .hljs-title { + color: #50fa7b !important; + } + .hljs-attr { + color: #50fa7b !important; + } + .hljs-tag { + color: #ff79c6 !important; + } + + .insights-container { + overflow-y: auto; + padding: 12px 16px 16px 16px; + position: relative; + z-index: 1; + min-height: 150px; + max-height: 600px; + flex: 1; + } + + /* Visibility handled by parent component */ + + .insights-container::-webkit-scrollbar { + width: 8px; + } + .insights-container::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.1); + border-radius: 4px; + } + .insights-container::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3); + border-radius: 4px; + } + .insights-container::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.5); + } + + insights-title { + color: rgba(255, 255, 255, 0.8); + font-size: 15px; + font-weight: 500; + font-family: 'Helvetica Neue', sans-serif; + margin: 12px 0 8px 0; + display: block; + } + + .insights-container h4 { + color: #ffffff; + font-size: 12px; + font-weight: 600; + margin: 12px 0 8px 0; + padding: 4px 8px; + border-radius: 4px; + background: transparent; + cursor: default; + } + + .insights-container h4:hover { + background: transparent; + } + + .insights-container h4:first-child { + margin-top: 0; + } + + .outline-item { + color: #ffffff; + font-size: 11px; + line-height: 1.4; + margin: 4px 0; + padding: 6px 8px; + border-radius: 4px; + background: transparent; + transition: background-color 0.15s ease; + cursor: pointer; + word-wrap: break-word; + } + + .outline-item:hover { + background: rgba(255, 255, 255, 0.1); + } + + .request-item { + color: #ffffff; + font-size: 12px; + line-height: 1.2; + margin: 4px 0; + padding: 6px 8px; + border-radius: 4px; + background: transparent; + cursor: default; + word-wrap: break-word; + transition: background-color 0.15s ease; + } + + .request-item.clickable { + cursor: pointer; + transition: all 0.15s ease; + } + .request-item.clickable:hover { + background: rgba(255, 255, 255, 0.1); + transform: translateX(2px); + } + + /* λ§ˆν¬λ‹€μš΄ λ Œλ”λ§λœ μ½˜ν…μΈ  μŠ€νƒ€μΌ */ + .markdown-content { + color: #ffffff; + font-size: 11px; + line-height: 1.4; + margin: 4px 0; + padding: 6px 8px; + border-radius: 4px; + background: transparent; + cursor: pointer; + word-wrap: break-word; + transition: all 0.15s ease; + } + + .markdown-content:hover { + background: rgba(255, 255, 255, 0.1); + transform: translateX(2px); + } + + .markdown-content p { + margin: 4px 0; + } + + .markdown-content ul, + .markdown-content ol { + margin: 4px 0; + padding-left: 16px; + } + + .markdown-content li { + margin: 2px 0; + } + + .markdown-content a { + color: #8be9fd; + text-decoration: none; + } + + .markdown-content a:hover { + text-decoration: underline; + } + + .markdown-content strong { + font-weight: 600; + color: #f8f8f2; + } + + .markdown-content em { + font-style: italic; + color: #f1fa8c; + } + + .empty-state { + display: flex; + align-items: center; + justify-content: center; + height: 100px; + color: rgba(255, 255, 255, 0.6); + font-size: 12px; + font-style: italic; + } + `; + + static properties = { + structuredData: { type: Object }, + isVisible: { type: Boolean }, + hasCompletedRecording: { type: Boolean }, + }; + + constructor() { + super(); + this.structuredData = { + summary: [], + topic: { header: '', bullets: [] }, + actions: [], + followUps: [], + }; + this.isVisible = true; + this.hasCompletedRecording = false; + + // λ§ˆν¬λ‹€μš΄ 라이브러리 μ΄ˆκΈ°ν™” + this.marked = null; + this.hljs = null; + this.isLibrariesLoaded = false; + this.DOMPurify = null; + this.isDOMPurifyLoaded = false; + + this.loadLibraries(); + } + + connectedCallback() { + super.connectedCallback(); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.on('update-structured-data', (event, data) => { + this.structuredData = data; + this.requestUpdate(); + }); + } + } + + disconnectedCallback() { + super.disconnectedCallback(); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.removeAllListeners('update-structured-data'); + } + } + + // Handle session reset from parent + resetAnalysis() { + this.structuredData = { + summary: [], + topic: { header: '', bullets: [] }, + actions: [], + followUps: [], + }; + this.requestUpdate(); + } + + async loadLibraries() { + try { + if (!window.marked) { + await this.loadScript('../../../assets/marked-4.3.0.min.js'); + } + + if (!window.hljs) { + await this.loadScript('../../../assets/highlight-11.9.0.min.js'); + } + + if (!window.DOMPurify) { + await this.loadScript('../../../assets/dompurify-3.0.7.min.js'); + } + + this.marked = window.marked; + this.hljs = window.hljs; + this.DOMPurify = window.DOMPurify; + + if (this.marked && this.hljs) { + this.marked.setOptions({ + highlight: (code, lang) => { + if (lang && this.hljs.getLanguage(lang)) { + try { + return this.hljs.highlight(code, { language: lang }).value; + } catch (err) { + console.warn('Highlight error:', err); + } + } + try { + return this.hljs.highlightAuto(code).value; + } catch (err) { + console.warn('Auto highlight error:', err); + } + return code; + }, + breaks: true, + gfm: true, + pedantic: false, + smartypants: false, + xhtml: false, + }); + + this.isLibrariesLoaded = true; + console.log('Markdown libraries loaded successfully'); + } + + if (this.DOMPurify) { + this.isDOMPurifyLoaded = true; + console.log('DOMPurify loaded successfully in SummaryView'); + } + } catch (error) { + console.error('Failed to load libraries:', error); + } + } + + loadScript(src) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = src; + script.onload = resolve; + script.onerror = reject; + document.head.appendChild(script); + }); + } + + parseMarkdown(text) { + if (!text) return ''; + + if (!this.isLibrariesLoaded || !this.marked) { + return text; + } + + try { + return this.marked(text); + } catch (error) { + console.error('Markdown parsing error:', error); + return text; + } + } + + handleMarkdownClick(originalText) { + this.handleRequestClick(originalText); + } + + renderMarkdownContent() { + if (!this.isLibrariesLoaded || !this.marked) { + return; + } + + const markdownElements = this.shadowRoot.querySelectorAll('[data-markdown-id]'); + markdownElements.forEach(element => { + const originalText = element.getAttribute('data-original-text'); + if (originalText) { + try { + let parsedHTML = this.parseMarkdown(originalText); + + if (this.isDOMPurifyLoaded && this.DOMPurify) { + parsedHTML = this.DOMPurify.sanitize(parsedHTML); + + if (this.DOMPurify.removed && this.DOMPurify.removed.length > 0) { + console.warn('Unsafe content detected in insights, showing plain text'); + element.textContent = '⚠️ ' + originalText; + return; + } + } + + element.innerHTML = parsedHTML; + } catch (error) { + console.error('Error rendering markdown for element:', error); + element.textContent = originalText; + } + } + }); + } + + async handleRequestClick(requestText) { + console.log('πŸ”₯ Analysis request clicked:', requestText); + + if (window.require) { + const { ipcRenderer } = window.require('electron'); + + try { + const isAskViewVisible = await ipcRenderer.invoke('is-window-visible', 'ask'); + + if (!isAskViewVisible) { + await ipcRenderer.invoke('toggle-feature', 'ask'); + await new Promise(resolve => setTimeout(resolve, 100)); + } + + const result = await ipcRenderer.invoke('send-question-to-ask', requestText); + + if (result.success) { + console.log('βœ… Question sent to AskView successfully'); + } else { + console.error('❌ Failed to send question to AskView:', result.error); + } + } catch (error) { + console.error('❌ Error in handleRequestClick:', error); + } + } + } + + getSummaryText() { + const data = this.structuredData || { summary: [], topic: { header: '', bullets: [] }, actions: [] }; + let sections = []; + + if (data.summary && data.summary.length > 0) { + sections.push(`Current Summary:\n${data.summary.map(s => `β€’ ${s}`).join('\n')}`); + } + + if (data.topic && data.topic.header && data.topic.bullets.length > 0) { + sections.push(`\n${data.topic.header}:\n${data.topic.bullets.map(b => `β€’ ${b}`).join('\n')}`); + } + + if (data.actions && data.actions.length > 0) { + sections.push(`\nActions:\n${data.actions.map(a => `β–Έ ${a}`).join('\n')}`); + } + + if (data.followUps && data.followUps.length > 0) { + sections.push(`\nFollow-Ups:\n${data.followUps.map(f => `β–Έ ${f}`).join('\n')}`); + } + + return sections.join('\n\n').trim(); + } + + updated(changedProperties) { + super.updated(changedProperties); + this.renderMarkdownContent(); + } + + render() { + if (!this.isVisible) { + return html`
`; + } + + const data = this.structuredData || { + summary: [], + topic: { header: '', bullets: [] }, + actions: [], + }; + + const hasAnyContent = data.summary.length > 0 || data.topic.bullets.length > 0 || data.actions.length > 0; + + return html` +
+ ${!hasAnyContent + ? html`
No insights yet...
` + : html` + Current Summary + ${data.summary.length > 0 + ? data.summary + .slice(0, 5) + .map( + (bullet, index) => html` +
this.handleMarkdownClick(bullet)} + > + ${bullet} +
+ ` + ) + : html`
No content yet...
`} + ${data.topic.header + ? html` + ${data.topic.header} + ${data.topic.bullets + .slice(0, 3) + .map( + (bullet, index) => html` +
this.handleMarkdownClick(bullet)} + > + ${bullet} +
+ ` + )} + ` + : ''} + ${data.actions.length > 0 + ? html` + Actions + ${data.actions + .slice(0, 5) + .map( + (action, index) => html` +
this.handleMarkdownClick(action)} + > + ${action} +
+ ` + )} + ` + : ''} + ${this.hasCompletedRecording && data.followUps && data.followUps.length > 0 + ? html` + Follow-Ups + ${data.followUps.map( + (followUp, index) => html` +
this.handleMarkdownClick(followUp)} + > + ${followUp} +
+ ` + )} + ` + : ''} + `} +
+ `; + } +} + +customElements.define('summary-view', SummaryView); \ No newline at end of file diff --git a/src/features/listen/summary/repositories/index.js b/src/features/listen/summary/repositories/index.js new file mode 100644 index 0000000..d5bd3b3 --- /dev/null +++ b/src/features/listen/summary/repositories/index.js @@ -0,0 +1,5 @@ +const summaryRepository = require('./sqlite.repository'); + +module.exports = { + ...summaryRepository, +}; \ No newline at end of file diff --git a/src/features/listen/summary/repositories/sqlite.repository.js b/src/features/listen/summary/repositories/sqlite.repository.js new file mode 100644 index 0000000..008aa21 --- /dev/null +++ b/src/features/listen/summary/repositories/sqlite.repository.js @@ -0,0 +1,39 @@ +const sqliteClient = require('../../../../common/services/sqliteClient'); + +function saveSummary({ sessionId, tldr, text, bullet_json, action_json, model = 'gpt-4.1' }) { + return new Promise((resolve, reject) => { + try { + const db = sqliteClient.getDb(); + const now = Math.floor(Date.now() / 1000); + const query = ` + INSERT INTO summaries (session_id, generated_at, model, text, tldr, bullet_json, action_json, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(session_id) DO UPDATE SET + generated_at=excluded.generated_at, + model=excluded.model, + text=excluded.text, + tldr=excluded.tldr, + bullet_json=excluded.bullet_json, + action_json=excluded.action_json, + updated_at=excluded.updated_at + `; + + const result = db.prepare(query).run(sessionId, now, model, text, tldr, bullet_json, action_json, now); + resolve({ changes: result.changes }); + } catch (err) { + console.error('Error saving summary:', err); + reject(err); + } + }); +} + +function getSummaryBySessionId(sessionId) { + const db = sqliteClient.getDb(); + const query = "SELECT * FROM summaries WHERE session_id = ?"; + return db.prepare(query).get(sessionId) || null; +} + +module.exports = { + saveSummary, + getSummaryBySessionId, +}; \ No newline at end of file diff --git a/src/features/listen/summary/summaryService.js b/src/features/listen/summary/summaryService.js new file mode 100644 index 0000000..53d3b79 --- /dev/null +++ b/src/features/listen/summary/summaryService.js @@ -0,0 +1,355 @@ +const { BrowserWindow } = require('electron'); +const { getSystemPrompt } = require('../../../common/prompts/promptBuilder.js'); +const { createLLM } = require('../../../common/ai/factory'); +const authService = require('../../../common/services/authService'); +const sessionRepository = require('../../../common/repositories/session'); +const summaryRepository = require('./repositories'); +const { getStoredApiKey, getStoredProvider, getCurrentModelInfo } = require('../../../electron/windowManager'); + +class SummaryService { + constructor() { + this.previousAnalysisResult = null; + this.analysisHistory = []; + this.conversationHistory = []; + this.currentSessionId = null; + + // Callbacks + this.onAnalysisComplete = null; + this.onStatusUpdate = null; + } + + setCallbacks({ onAnalysisComplete, onStatusUpdate }) { + this.onAnalysisComplete = onAnalysisComplete; + this.onStatusUpdate = onStatusUpdate; + } + + setSessionId(sessionId) { + this.currentSessionId = sessionId; + } + + // async getApiKey() { + // const storedKey = await getStoredApiKey(); + // if (storedKey) { + // console.log('[SummaryService] Using stored API key'); + // return storedKey; + // } + + // const envKey = process.env.OPENAI_API_KEY; + // if (envKey) { + // console.log('[SummaryService] Using environment API key'); + // return envKey; + // } + + // console.error('[SummaryService] No API key found in storage or environment'); + // return null; + // } + + sendToRenderer(channel, data) { + BrowserWindow.getAllWindows().forEach(win => { + if (!win.isDestroyed()) { + win.webContents.send(channel, data); + } + }); + } + + addConversationTurn(speaker, text) { + const conversationText = `${speaker.toLowerCase()}: ${text.trim()}`; + this.conversationHistory.push(conversationText); + console.log(`πŸ’¬ Added conversation text: ${conversationText}`); + console.log(`πŸ“ˆ Total conversation history: ${this.conversationHistory.length} texts`); + + // Trigger analysis if needed + this.triggerAnalysisIfNeeded(); + } + + getConversationHistory() { + return this.conversationHistory; + } + + resetConversationHistory() { + this.conversationHistory = []; + this.previousAnalysisResult = null; + this.analysisHistory = []; + console.log('πŸ”„ Conversation history and analysis state reset'); + } + + /** + * Converts conversation history into text to include in the prompt. + * @param {Array} conversationTexts - Array of conversation texts ["me: ~~~", "them: ~~~", ...] + * @param {number} maxTurns - Maximum number of recent turns to include + * @returns {string} - Formatted conversation string for the prompt + */ + formatConversationForPrompt(conversationTexts, maxTurns = 30) { + if (conversationTexts.length === 0) return ''; + return conversationTexts.slice(-maxTurns).join('\n'); + } + + async makeOutlineAndRequests(conversationTexts, maxTurns = 30) { + console.log(`πŸ” makeOutlineAndRequests called - conversationTexts: ${conversationTexts.length}`); + + if (conversationTexts.length === 0) { + console.log('⚠️ No conversation texts available for analysis'); + return null; + } + + const recentConversation = this.formatConversationForPrompt(conversationTexts, maxTurns); + + // 이전 뢄석 κ²°κ³Όλ₯Ό ν”„λ‘¬ν”„νŠΈμ— 포함 + let contextualPrompt = ''; + if (this.previousAnalysisResult) { + contextualPrompt = ` +Previous Analysis Context: +- Main Topic: ${this.previousAnalysisResult.topic.header} +- Key Points: ${this.previousAnalysisResult.summary.slice(0, 3).join(', ')} +- Last Actions: ${this.previousAnalysisResult.actions.slice(0, 2).join(', ')} + +Please build upon this context while analyzing the new conversation segments. +`; + } + + const basePrompt = getSystemPrompt('pickle_glass_analysis', '', false); + const systemPrompt = basePrompt.replace('{{CONVERSATION_HISTORY}}', recentConversation); + + try { + if (this.currentSessionId) { + await sessionRepository.touch(this.currentSessionId); + } + + const modelInfo = await getCurrentModelInfo(null, { type: 'llm' }); + if (!modelInfo || !modelInfo.apiKey) { + throw new Error('AI model or API key is not configured.'); + } + console.log(`πŸ€– Sending analysis request to ${modelInfo.provider} using model ${modelInfo.model}`); + + const messages = [ + { + role: 'system', + content: systemPrompt, + }, + { + role: 'user', + content: `${contextualPrompt} + +Analyze the conversation and provide a structured summary. Format your response as follows: + +**Summary Overview** +- Main discussion point with context + +**Key Topic: [Topic Name]** +- First key insight +- Second key insight +- Third key insight + +**Extended Explanation** +Provide 2-3 sentences explaining the context and implications. + +**Suggested Questions** +1. First follow-up question? +2. Second follow-up question? +3. Third follow-up question? + +Keep all points concise and build upon previous analysis if provided.`, + }, + ]; + + console.log('πŸ€– Sending analysis request to AI...'); + + const llm = createLLM(modelInfo.provider, { + apiKey: modelInfo.apiKey, + model: modelInfo.model, + temperature: 0.7, + maxTokens: 1024, + usePortkey: modelInfo.provider === 'openai-glass', + portkeyVirtualKey: modelInfo.provider === 'openai-glass' ? modelInfo.apiKey : undefined, + }); + + const completion = await llm.chat(messages); + + const responseText = completion.content; + console.log(`βœ… Analysis response received: ${responseText}`); + const structuredData = this.parseResponseText(responseText, this.previousAnalysisResult); + + if (this.currentSessionId) { + try { + summaryRepository.saveSummary({ + sessionId: this.currentSessionId, + text: responseText, + tldr: structuredData.summary.join('\n'), + bullet_json: JSON.stringify(structuredData.topic.bullets), + action_json: JSON.stringify(structuredData.actions), + model: modelInfo.model + }); + } catch (err) { + console.error('[DB] Failed to save summary:', err); + } + } + + // 뢄석 κ²°κ³Ό μ €μž₯ + this.previousAnalysisResult = structuredData; + this.analysisHistory.push({ + timestamp: Date.now(), + data: structuredData, + conversationLength: conversationTexts.length, + }); + + if (this.analysisHistory.length > 10) { + this.analysisHistory.shift(); + } + + return structuredData; + } catch (error) { + console.error('❌ Error during analysis generation:', error.message); + return this.previousAnalysisResult; // μ—λŸ¬ μ‹œ 이전 κ²°κ³Ό λ°˜ν™˜ + } + } + + parseResponseText(responseText, previousResult) { + const structuredData = { + summary: [], + topic: { header: '', bullets: [] }, + actions: [], + followUps: ['βœ‰οΈ Draft a follow-up email', 'βœ… Generate action items', 'πŸ“ Show summary'], + }; + + // 이전 κ²°κ³Όκ°€ 있으면 κΈ°λ³Έκ°’μœΌλ‘œ μ‚¬μš© + if (previousResult) { + structuredData.topic.header = previousResult.topic.header; + structuredData.summary = [...previousResult.summary]; + } + + try { + const lines = responseText.split('\n'); + let currentSection = ''; + let isCapturingTopic = false; + let topicName = ''; + + for (const line of lines) { + const trimmedLine = line.trim(); + + // μ„Ήμ…˜ 헀더 감지 + if (trimmedLine.startsWith('**Summary Overview**')) { + currentSection = 'summary-overview'; + continue; + } else if (trimmedLine.startsWith('**Key Topic:')) { + currentSection = 'topic'; + isCapturingTopic = true; + topicName = trimmedLine.match(/\*\*Key Topic: (.+?)\*\*/)?.[1] || ''; + if (topicName) { + structuredData.topic.header = topicName + ':'; + } + continue; + } else if (trimmedLine.startsWith('**Extended Explanation**')) { + currentSection = 'explanation'; + continue; + } else if (trimmedLine.startsWith('**Suggested Questions**')) { + currentSection = 'questions'; + continue; + } + + // 컨텐츠 νŒŒμ‹± + if (trimmedLine.startsWith('-') && currentSection === 'summary-overview') { + const summaryPoint = trimmedLine.substring(1).trim(); + if (summaryPoint && !structuredData.summary.includes(summaryPoint)) { + // κΈ°μ‘΄ summary μ—…λ°μ΄νŠΈ (μ΅œλŒ€ 5개 μœ μ§€) + structuredData.summary.unshift(summaryPoint); + if (structuredData.summary.length > 5) { + structuredData.summary.pop(); + } + } + } else if (trimmedLine.startsWith('-') && currentSection === 'topic') { + const bullet = trimmedLine.substring(1).trim(); + if (bullet && structuredData.topic.bullets.length < 3) { + structuredData.topic.bullets.push(bullet); + } + } else if (currentSection === 'explanation' && trimmedLine) { + // explanation을 topic bullets에 μΆ”κ°€ (λ¬Έμž₯ λ‹¨μœ„λ‘œ) + const sentences = trimmedLine + .split(/\.\s+/) + .filter(s => s.trim().length > 0) + .map(s => s.trim() + (s.endsWith('.') ? '' : '.')); + + sentences.forEach(sentence => { + if (structuredData.topic.bullets.length < 3 && !structuredData.topic.bullets.includes(sentence)) { + structuredData.topic.bullets.push(sentence); + } + }); + } else if (trimmedLine.match(/^\d+\./) && currentSection === 'questions') { + const question = trimmedLine.replace(/^\d+\.\s*/, '').trim(); + if (question && question.includes('?')) { + structuredData.actions.push(`❓ ${question}`); + } + } + } + + // κΈ°λ³Έ μ•‘μ…˜ μΆ”κ°€ + const defaultActions = ['✨ What should I say next?', 'πŸ’¬ Suggest follow-up questions']; + defaultActions.forEach(action => { + if (!structuredData.actions.includes(action)) { + structuredData.actions.push(action); + } + }); + + // μ•‘μ…˜ 개수 μ œν•œ + structuredData.actions = structuredData.actions.slice(0, 5); + + // μœ νš¨μ„± 검증 및 이전 데이터 병합 + if (structuredData.summary.length === 0 && previousResult) { + structuredData.summary = previousResult.summary; + } + if (structuredData.topic.bullets.length === 0 && previousResult) { + structuredData.topic.bullets = previousResult.topic.bullets; + } + } catch (error) { + console.error('❌ Error parsing response text:', error); + // μ—λŸ¬ μ‹œ 이전 κ²°κ³Ό λ°˜ν™˜ + return ( + previousResult || { + summary: [], + topic: { header: 'Analysis in progress', bullets: [] }, + actions: ['✨ What should I say next?', 'πŸ’¬ Suggest follow-up questions'], + followUps: ['βœ‰οΈ Draft a follow-up email', 'βœ… Generate action items', 'πŸ“ Show summary'], + } + ); + } + + console.log('πŸ“Š Final structured data:', JSON.stringify(structuredData, null, 2)); + return structuredData; + } + + /** + * Triggers analysis when conversation history reaches 5 texts. + */ + async triggerAnalysisIfNeeded() { + if (this.conversationHistory.length >= 5 && this.conversationHistory.length % 5 === 0) { + console.log(`πŸš€ Triggering analysis (non-blocking) - ${this.conversationHistory.length} conversation texts accumulated`); + + this.makeOutlineAndRequests(this.conversationHistory) + .then(data => { + if (data) { + console.log('πŸ“€ Sending structured data to renderer'); + this.sendToRenderer('update-structured-data', data); + + // Notify callback + if (this.onAnalysisComplete) { + this.onAnalysisComplete(data); + } + } else { + console.log('❌ No analysis data returned from non-blocking call'); + } + }) + .catch(error => { + console.error('❌ Error in non-blocking analysis:', error); + }); + } + } + + getCurrentAnalysisData() { + return { + previousResult: this.previousAnalysisResult, + history: this.analysisHistory, + conversationLength: this.conversationHistory.length, + }; + } +} + +module.exports = SummaryService; \ No newline at end of file diff --git a/src/features/onboarding/OnboardingView.js b/src/features/onboarding/OnboardingView.js deleted file mode 100644 index 82d5eb4..0000000 --- a/src/features/onboarding/OnboardingView.js +++ /dev/null @@ -1,372 +0,0 @@ -import { html, css, LitElement } from '../../assets/lit-core-2.7.4.min.js'; - -export class OnboardingView extends LitElement { - static styles = css` - * { - font-family: 'Helvetica Neue', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - cursor: default; - user-select: none; - } - - :host { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - position: relative; - overflow: hidden; - } - - .slide { - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - padding: 20px; - text-align: left; - border-radius: 16px; - height: 100%; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 50px; - transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease; - opacity: 0; - transform: translateX(100%); - } - - .slide.active { - opacity: 1; - transform: translateX(0); - } - - .slide.prev { - transform: translateX(-100%); - } - - .slide-1 { - background: linear-gradient(135deg, #2d1b69 0%, #11998e 100%); - color: white; - } - - .slide-2 { - background: linear-gradient(135deg, #8e2de2 0%, #4a00e0 100%); - color: white; - } - - .slide-3 { - background: linear-gradient(135deg, #2c5aa0 0%, #1a237e 100%); - color: white; - } - - .slide-4 { - background: linear-gradient(135deg, #fc466b 0%, #3f5efb 100%); - color: white; - } - - .slide-5 { - background: linear-gradient(135deg, #833ab4 0%, #fd1d1d 100%); - color: white; - } - - .slide-title { - font-size: 24px; - font-weight: 700; - margin-bottom: 12px; - margin-top: 10px; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - - .slide-content { - font-size: 14px; - line-height: 1.5; - max-width: 100%; - margin-bottom: 20px; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); - } - - .context-textarea { - width: 100%; - max-width: 100%; - height: 80px; - padding: 12px; - border: 2px solid rgba(255, 255, 255, 0.3); - border-radius: 8px; - background: rgba(255, 255, 255, 0.1); - color: white; - font-size: 14px; - resize: vertical; - box-sizing: border-box; - } - - .context-textarea::placeholder { - color: rgba(255, 255, 255, 0.7); - } - - .context-textarea:focus { - outline: none; - border-color: rgba(255, 255, 255, 0.6); - background: rgba(255, 255, 255, 0.15); - } - - .navigation { - position: absolute; - bottom: 0; - left: 0; - right: 0; - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 16px; - } - - .nav-button { - background: rgba(255, 255, 255, 0.2); - border: 1px solid rgba(255, 255, 255, 0.3); - color: white; - padding: 8px 16px; - border-radius: 20px; - font-size: 12px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - display: flex; - align-items: center; - justify-content: center; - } - - .nav-button:hover { - background: rgba(255, 255, 255, 0.3); - border-color: rgba(255, 255, 255, 0.5); - } - - .nav-button:disabled { - opacity: 0.5; - cursor: not-allowed; - transform: none; - } - - .nav-button:disabled:hover { - background: rgba(255, 255, 255, 0.2); - border-color: rgba(255, 255, 255, 0.3); - } - - .progress-dots { - display: flex; - gap: 12px; - align-items: center; - } - - .dot { - width: 10px; - height: 10px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.3); - transition: all 0.3s ease; - } - - .dot.active { - background: white; - transform: scale(1.2); - } - - .emoji { - position: absolute; - top: 15px; - right: 15px; - font-size: 40px; - transform: rotate(15deg); - z-index: 1; - } - - .feature-list { - text-align: left; - max-width: 100%; - } - - .feature-item { - display: flex; - align-items: center; - margin-bottom: 8px; - font-size: 14px; - } - - .feature-icon { - font-size: 16px; - margin-right: 8px; - } - `; - - static properties = { - currentSlide: { type: Number }, - contextText: { type: String }, - onComplete: { type: Function }, - onClose: { type: Function }, - }; - - constructor() { - super(); - this.currentSlide = 0; - this.contextText = ''; - this.onComplete = () => {}; - this.onClose = () => {}; - } - - nextSlide() { - if (this.currentSlide < 4) { - this.currentSlide++; - } else { - this.completeOnboarding(); - } - } - - prevSlide() { - if (this.currentSlide > 0) { - this.currentSlide--; - } - } - - handleContextInput(e) { - this.contextText = e.target.value; - } - - completeOnboarding() { - // Save the context text to localStorage as custom prompt - if (this.contextText.trim()) { - localStorage.setItem('customPrompt', this.contextText.trim()); - } - - // Mark onboarding as completed - localStorage.setItem('onboardingCompleted', 'true'); - - // Call the completion callback - this.onComplete(); - } - - renderSlide1() { - return html` -
-
πŸ‘‹
-
Welcome to Pickle Glass!
-
- Pickle Glass hears what you hear and sees what you see, then generates AI-powered suggestions without any user input needed. -
-
- `; - } - - renderSlide2() { - return html` -
-
πŸ”’
-
Completely Private
-
- Your secret weapon is completely invisible! It won't show up on screen sharing apps, keeping your assistance completely private - during interviews and meetings. -
-
- `; - } - - renderSlide3() { - return html` -
-
πŸ“
-
Tell Us Your Context
-
- Help the AI understand your situation better by sharing your context - like your resume, the job description, or interview - details. -
- -
- `; - } - - renderSlide4() { - return html` -
-
βš™οΈ
-
Explore More Features
-
-
- 🎨 - Customize settings and AI profiles -
-
- πŸ“š - View your conversation history -
-
- πŸ”§ - Adjust screenshot intervals and quality -
-
-
- `; - } - - renderSlide5() { - return html` -
-
πŸŽ‰
-
You're All Set!
-
- Pickle Glass is completely free to use. Just add your openai API key and start getting AI-powered assistance in your interviews - and meetings! -
-
- `; - } - - render() { - return html` - ${this.renderSlide1()} ${this.renderSlide2()} ${this.renderSlide3()} ${this.renderSlide4()} ${this.renderSlide5()} - - - `; - } -} - -customElements.define('onboarding-view', OnboardingView); diff --git a/src/features/settings/SettingsView.js b/src/features/settings/SettingsView.js new file mode 100644 index 0000000..7dbd9f1 --- /dev/null +++ b/src/features/settings/SettingsView.js @@ -0,0 +1,1202 @@ +import { html, css, LitElement } from '../../assets/lit-core-2.7.4.min.js'; + +export class SettingsView extends LitElement { + static styles = css` + * { + font-family: 'Helvetica Neue', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + cursor: default; + user-select: none; + } + + :host { + display: block; + width: 240px; + height: 100%; + color: white; + } + + .settings-container { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + background: rgba(20, 20, 20, 0.8); + border-radius: 12px; + outline: 0.5px rgba(255, 255, 255, 0.2) solid; + outline-offset: -1px; + box-sizing: border-box; + position: relative; + overflow-y: auto; + padding: 12px 12px; + z-index: 1000; + } + + .settings-container::-webkit-scrollbar { + width: 6px; + } + + .settings-container::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + } + + .settings-container::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.2); + border-radius: 3px; + } + + .settings-container::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.3); + } + + .settings-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.15); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + border-radius: 12px; + filter: blur(10px); + z-index: -1; + } + + .settings-button[disabled], + .api-key-section input[disabled] { + opacity: 0.4; + cursor: not-allowed; + pointer-events: none; + } + + .header-section { + display: flex; + justify-content: space-between; + align-items: flex-start; + padding-bottom: 6px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + position: relative; + z-index: 1; + } + + .title-line { + display: flex; + justify-content: space-between; + align-items: center; + } + + .app-title { + font-size: 13px; + font-weight: 500; + color: white; + margin: 0 0 4px 0; + } + + .account-info { + font-size: 11px; + color: rgba(255, 255, 255, 0.7); + margin: 0; + } + + .invisibility-icon { + padding-top: 2px; + opacity: 0; + transition: opacity 0.3s ease; + } + + .invisibility-icon.visible { + opacity: 1; + } + + .invisibility-icon svg { + width: 16px; + height: 16px; + } + + .shortcuts-section { + display: flex; + flex-direction: column; + gap: 2px; + padding: 4px 0; + position: relative; + z-index: 1; + } + + .shortcut-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 0; + color: white; + font-size: 11px; + } + + .shortcut-name { + font-weight: 300; + } + + .shortcut-keys { + display: flex; + align-items: center; + gap: 3px; + } + + .cmd-key, .shortcut-key { + background: rgba(255, 255, 255, 0.1); + border-radius: 3px; + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: 500; + color: rgba(255, 255, 255, 0.9); + } + + /* Buttons Section */ + .buttons-section { + display: flex; + flex-direction: column; + gap: 4px; + padding-top: 6px; + border-top: 1px solid rgba(255, 255, 255, 0.1); + position: relative; + z-index: 1; + flex: 1; + } + + .settings-button { + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 4px; + color: white; + padding: 5px 10px; + font-size: 11px; + font-weight: 400; + cursor: pointer; + transition: all 0.15s ease; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + } + + .settings-button:hover { + background: rgba(255, 255, 255, 0.15); + border-color: rgba(255, 255, 255, 0.3); + } + + .settings-button:active { + transform: translateY(1px); + } + + .settings-button.full-width { + width: 100%; + } + + .settings-button.half-width { + flex: 1; + } + + .settings-button.danger { + background: rgba(255, 59, 48, 0.1); + border-color: rgba(255, 59, 48, 0.3); + color: rgba(255, 59, 48, 0.9); + } + + .settings-button.danger:hover { + background: rgba(255, 59, 48, 0.15); + border-color: rgba(255, 59, 48, 0.4); + } + + .move-buttons, .bottom-buttons { + display: flex; + gap: 4px; + } + + .api-key-section { + padding: 6px 0; + border-top: 1px solid rgba(255, 255, 255, 0.1); + } + + .api-key-section input { + width: 100%; + background: rgba(0,0,0,0.2); + border: 1px solid rgba(255,255,255,0.2); + color: white; + border-radius: 4px; + padding: 4px; + font-size: 11px; + margin-bottom: 4px; + box-sizing: border-box; + } + + .api-key-section input::placeholder { + color: rgba(255, 255, 255, 0.4); + } + + /* Preset Management Section */ + .preset-section { + padding: 6px 0; + border-top: 1px solid rgba(255, 255, 255, 0.1); + } + + .preset-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 4px; + } + + .preset-title { + font-size: 11px; + font-weight: 500; + color: white; + } + + .preset-count { + font-size: 9px; + color: rgba(255, 255, 255, 0.5); + margin-left: 4px; + } + + .preset-toggle { + font-size: 10px; + color: rgba(255, 255, 255, 0.6); + cursor: pointer; + padding: 2px 4px; + border-radius: 2px; + transition: background-color 0.15s ease; + } + + .preset-toggle:hover { + background: rgba(255, 255, 255, 0.1); + } + + .preset-list { + display: flex; + flex-direction: column; + gap: 2px; + max-height: 120px; + overflow-y: auto; + } + + .preset-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 6px; + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; + cursor: pointer; + transition: all 0.15s ease; + font-size: 11px; + border: 1px solid transparent; + } + + .preset-item:hover { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.1); + } + + .preset-item.selected { + background: rgba(0, 122, 255, 0.25); + border-color: rgba(0, 122, 255, 0.6); + box-shadow: 0 0 0 1px rgba(0, 122, 255, 0.3); + } + + .preset-name { + color: white; + flex: 1; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + font-weight: 300; + } + + .preset-item.selected .preset-name { + font-weight: 500; + } + + .preset-status { + font-size: 9px; + color: rgba(0, 122, 255, 0.8); + font-weight: 500; + margin-left: 6px; + } + + .no-presets-message { + padding: 12px 8px; + text-align: center; + color: rgba(255, 255, 255, 0.5); + font-size: 10px; + line-height: 1.4; + } + + .no-presets-message .web-link { + color: rgba(0, 122, 255, 0.8); + text-decoration: underline; + cursor: pointer; + } + + .no-presets-message .web-link:hover { + color: rgba(0, 122, 255, 1); + } + + .loading-state { + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + color: rgba(255, 255, 255, 0.7); + font-size: 11px; + } + + .loading-spinner { + width: 12px; + height: 12px; + border: 1px solid rgba(255, 255, 255, 0.2); + border-top: 1px solid rgba(255, 255, 255, 0.8); + border-radius: 50%; + animation: spin 1s linear infinite; + margin-right: 6px; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + .hidden { + display: none; + } + + .api-key-section, .model-selection-section { + padding: 8px 0; + border-top: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + flex-direction: column; + gap: 10px; + } + .provider-key-group, .model-select-group { + display: flex; + flex-direction: column; + gap: 4px; + } + label { + font-size: 11px; + font-weight: 500; + color: rgba(255, 255, 255, 0.8); + margin-left: 2px; + } + label > strong { + color: white; + font-weight: 600; + } + .provider-key-group input { + width: 100%; background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.2); + color: white; border-radius: 4px; padding: 5px 8px; font-size: 11px; box-sizing: border-box; + } + .key-buttons { display: flex; gap: 4px; } + .key-buttons .settings-button { flex: 1; padding: 4px; } + .model-list { + display: flex; flex-direction: column; gap: 2px; max-height: 120px; + overflow-y: auto; background: rgba(0,0,0,0.3); border-radius: 4px; + padding: 4px; margin-top: 4px; + } + .model-item { padding: 5px 8px; font-size: 11px; border-radius: 3px; cursor: pointer; transition: background-color 0.15s; } + .model-item:hover { background-color: rgba(255,255,255,0.1); } + .model-item.selected { background-color: rgba(0, 122, 255, 0.4); font-weight: 500; } + + /* ────────────────[ GLASS BYPASS ]─────────────── */ + :host-context(body.has-glass) { + animation: none !important; + transition: none !important; + transform: none !important; + will-change: auto !important; + } + + :host-context(body.has-glass) * { + background: transparent !important; + filter: none !important; + backdrop-filter: none !important; + box-shadow: none !important; + outline: none !important; + border: none !important; + border-radius: 0 !important; + transition: none !important; + animation: none !important; + } + + :host-context(body.has-glass) .settings-container::before { + display: none !important; + } + `; + + + //////// after_modelStateService //////// + static properties = { + shortcuts: { type: Object, state: true }, + firebaseUser: { type: Object, state: true }, + isLoading: { type: Boolean, state: true }, + isContentProtectionOn: { type: Boolean, state: true }, + saving: { type: Boolean, state: true }, + providerConfig: { type: Object, state: true }, + apiKeys: { type: Object, state: true }, + availableLlmModels: { type: Array, state: true }, + availableSttModels: { type: Array, state: true }, + selectedLlm: { type: String, state: true }, + selectedStt: { type: String, state: true }, + isLlmListVisible: { type: Boolean }, + isSttListVisible: { type: Boolean }, + presets: { type: Array, state: true }, + selectedPreset: { type: Object, state: true }, + showPresets: { type: Boolean, state: true }, + }; + //////// after_modelStateService //////// + + constructor() { + super(); + //////// after_modelStateService //////// + this.shortcuts = {}; + this.firebaseUser = null; + this.apiKeys = { openai: '', gemini: '', anthropic: '' }; + this.providerConfig = {}; + this.isLoading = true; + this.isContentProtectionOn = true; + this.saving = false; + this.availableLlmModels = []; + this.availableSttModels = []; + this.selectedLlm = null; + this.selectedStt = null; + this.isLlmListVisible = false; + this.isSttListVisible = false; + this.presets = []; + this.selectedPreset = null; + this.showPresets = false; + this.handleUsePicklesKey = this.handleUsePicklesKey.bind(this) + this.loadInitialData(); + //////// after_modelStateService //////// + } + + //////// after_modelStateService //////// + async loadInitialData() { + if (!window.require) return; + this.isLoading = true; + const { ipcRenderer } = window.require('electron'); + try { + const [userState, config, storedKeys, availableLlm, availableStt, selectedModels, presets, contentProtection, shortcuts] = await Promise.all([ + ipcRenderer.invoke('get-current-user'), + ipcRenderer.invoke('model:get-provider-config'), // Provider μ„€μ • λ‘œλ“œ + ipcRenderer.invoke('model:get-all-keys'), + ipcRenderer.invoke('model:get-available-models', { type: 'llm' }), + ipcRenderer.invoke('model:get-available-models', { type: 'stt' }), + ipcRenderer.invoke('model:get-selected-models'), + ipcRenderer.invoke('settings:getPresets'), + ipcRenderer.invoke('get-content-protection-status'), + ipcRenderer.invoke('get-current-shortcuts') + ]); + + if (userState && userState.isLoggedIn) this.firebaseUser = userState; + this.providerConfig = config; + this.apiKeys = storedKeys; + this.availableLlmModels = availableLlm; + this.availableSttModels = availableStt; + this.selectedLlm = selectedModels.llm; + this.selectedStt = selectedModels.stt; + this.presets = presets || []; + this.isContentProtectionOn = contentProtection; + this.shortcuts = shortcuts || {}; + if (this.presets.length > 0) { + const firstUserPreset = this.presets.find(p => p.is_default === 0); + if (firstUserPreset) this.selectedPreset = firstUserPreset; + } + } catch (error) { + console.error('Error loading initial settings data:', error); + } finally { + this.isLoading = false; + } + } + + async handleSaveKey(provider) { + const input = this.shadowRoot.querySelector(`#key-input-${provider}`); + if (!input) return; + const key = input.value; + this.saving = true; + + const { ipcRenderer } = window.require('electron'); + const result = await ipcRenderer.invoke('model:validate-key', { provider, key }); + + if (result.success) { + this.apiKeys = { ...this.apiKeys, [provider]: key }; + await this.refreshModelData(); + } else { + alert(`Failed to save ${provider} key: ${result.error}`); + input.value = this.apiKeys[provider] || ''; + } + this.saving = false; + } + + async handleClearKey(provider) { + this.saving = true; + const { ipcRenderer } = window.require('electron'); + await ipcRenderer.invoke('model:remove-api-key', { provider }); + this.apiKeys = { ...this.apiKeys, [provider]: '' }; + await this.refreshModelData(); + this.saving = false; + } + + async refreshModelData() { + const { ipcRenderer } = window.require('electron'); + const [availableLlm, availableStt, selected] = await Promise.all([ + ipcRenderer.invoke('model:get-available-models', { type: 'llm' }), + ipcRenderer.invoke('model:get-available-models', { type: 'stt' }), + ipcRenderer.invoke('model:get-selected-models') + ]); + this.availableLlmModels = availableLlm; + this.availableSttModels = availableStt; + this.selectedLlm = selected.llm; + this.selectedStt = selected.stt; + this.requestUpdate(); + } + + async toggleModelList(type) { + const visibilityProp = type === 'llm' ? 'isLlmListVisible' : 'isSttListVisible'; + + if (!this[visibilityProp]) { + this.saving = true; + this.requestUpdate(); + + await this.refreshModelData(); + + this.saving = false; + } + + // 데이터 μƒˆλ‘œκ³ μΉ¨ ν›„, λͺ©λ‘μ˜ ν‘œμ‹œ μƒνƒœλ₯Ό ν† κΈ€ν•©λ‹ˆλ‹€. + this[visibilityProp] = !this[visibilityProp]; + this.requestUpdate(); + } + + async selectModel(type, modelId) { + this.saving = true; + const { ipcRenderer } = window.require('electron'); + await ipcRenderer.invoke('model:set-selected-model', { type, modelId }); + if (type === 'llm') this.selectedLlm = modelId; + if (type === 'stt') this.selectedStt = modelId; + this.isLlmListVisible = false; + this.isSttListVisible = false; + this.saving = false; + this.requestUpdate(); + } + + handleUsePicklesKey(e) { + e.preventDefault() + if (this.wasJustDragged) return + + console.log("Requesting Firebase authentication from main process...") + if (window.require) { + window.require("electron").ipcRenderer.invoke("start-firebase-auth") + } + } + //////// after_modelStateService //////// + + openShortcutEditor() { + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('open-shortcut-editor'); + } + } + + connectedCallback() { + super.connectedCallback(); + + this.setupEventListeners(); + this.setupIpcListeners(); + this.setupWindowResize(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.cleanupEventListeners(); + this.cleanupIpcListeners(); + this.cleanupWindowResize(); + } + + setupEventListeners() { + this.addEventListener('mouseenter', this.handleMouseEnter); + this.addEventListener('mouseleave', this.handleMouseLeave); + } + + cleanupEventListeners() { + this.removeEventListener('mouseenter', this.handleMouseEnter); + this.removeEventListener('mouseleave', this.handleMouseLeave); + } + + setupIpcListeners() { + if (!window.require) return; + + const { ipcRenderer } = window.require('electron'); + + this._userStateListener = (event, userState) => { + console.log('[SettingsView] Received user-state-changed:', userState); + if (userState && userState.isLoggedIn) { + this.firebaseUser = userState; + } else { + this.firebaseUser = null; + } + this.requestUpdate(); + }; + + this._settingsUpdatedListener = (event, settings) => { + console.log('[SettingsView] Received settings-updated'); + this.settings = settings; + this.requestUpdate(); + }; + + // 프리셋 μ—…λ°μ΄νŠΈ λ¦¬μŠ€λ„ˆ μΆ”κ°€ + this._presetsUpdatedListener = async (event) => { + console.log('[SettingsView] Received presets-updated, refreshing presets'); + try { + const presets = await ipcRenderer.invoke('settings:getPresets'); + this.presets = presets || []; + + // ν˜„μž¬ μ„ νƒλœ 프리셋이 μ‚­μ œλ˜μ—ˆλŠ”μ§€ 확인 (μ‚¬μš©μž ν”„λ¦¬μ…‹λ§Œ κ³ λ €) + const userPresets = this.presets.filter(p => p.is_default === 0); + if (this.selectedPreset && !userPresets.find(p => p.id === this.selectedPreset.id)) { + this.selectedPreset = userPresets.length > 0 ? userPresets[0] : null; + } + + this.requestUpdate(); + } catch (error) { + console.error('[SettingsView] Failed to refresh presets:', error); + } + }; + this._shortcutListener = (event, keybinds) => { + console.log('[SettingsView] Received updated shortcuts:', keybinds); + this.shortcuts = keybinds; + }; + + ipcRenderer.on('user-state-changed', this._userStateListener); + ipcRenderer.on('settings-updated', this._settingsUpdatedListener); + ipcRenderer.on('presets-updated', this._presetsUpdatedListener); + ipcRenderer.on('shortcuts-updated', this._shortcutListener); + } + + cleanupIpcListeners() { + if (!window.require) return; + + const { ipcRenderer } = window.require('electron'); + + if (this._userStateListener) { + ipcRenderer.removeListener('user-state-changed', this._userStateListener); + } + if (this._settingsUpdatedListener) { + ipcRenderer.removeListener('settings-updated', this._settingsUpdatedListener); + } + if (this._presetsUpdatedListener) { + ipcRenderer.removeListener('presets-updated', this._presetsUpdatedListener); + } + if (this._shortcutListener) { + ipcRenderer.removeListener('shortcuts-updated', this._shortcutListener); + } + } + + setupWindowResize() { + this.resizeHandler = () => { + this.requestUpdate(); + this.updateScrollHeight(); + }; + window.addEventListener('resize', this.resizeHandler); + + // Initial setup + setTimeout(() => this.updateScrollHeight(), 100); + } + + cleanupWindowResize() { + if (this.resizeHandler) { + window.removeEventListener('resize', this.resizeHandler); + } + } + + updateScrollHeight() { + const windowHeight = window.innerHeight; + const maxHeight = windowHeight; + + this.style.maxHeight = `${maxHeight}px`; + + const container = this.shadowRoot?.querySelector('.settings-container'); + if (container) { + container.style.maxHeight = `${maxHeight}px`; + } + } + + handleMouseEnter = () => { + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.send('cancel-hide-window', 'settings'); + } + } + + handleMouseLeave = () => { + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.send('hide-window', 'settings'); + } + } + + // getMainShortcuts() { + // return [ + // { name: 'Show / Hide', key: '\\' }, + // { name: 'Ask Anything', key: '↡' }, + // { name: 'Scroll AI Response', key: '↕' } + // ]; + // } + getMainShortcuts() { + return [ + { name: 'Show / Hide', accelerator: this.shortcuts.toggleVisibility }, + { name: 'Ask Anything', accelerator: this.shortcuts.nextStep }, + { name: 'Scroll Up Response', accelerator: this.shortcuts.scrollUp }, + { name: 'Scroll Down Response', accelerator: this.shortcuts.scrollDown }, + ]; + } + + renderShortcutKeys(accelerator) { + if (!accelerator) return html`N/A`; + + const keyMap = { + 'Cmd': '⌘', 'Command': '⌘', 'Ctrl': 'βŒƒ', 'Alt': 'βŒ₯', 'Shift': '⇧', 'Enter': '↡', + 'Up': '↑', 'Down': '↓', 'Left': '←', 'Right': 'β†’' + }; + + // scrollDown/scrollUp의 특수 처리 + if (accelerator.includes('↕')) { + const keys = accelerator.replace('↕','').split('+'); + keys.push('↕'); + return html`${keys.map(key => html`${keyMap[key] || key}`)}`; + } + + const keys = accelerator.split('+'); + return html`${keys.map(key => html`${keyMap[key] || key}`)}`; + } + + togglePresets() { + this.showPresets = !this.showPresets; + } + + async handlePresetSelect(preset) { + this.selectedPreset = preset; + // Here you could implement preset application logic + console.log('Selected preset:', preset); + } + + handleMoveLeft() { + console.log('Move Left clicked'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('move-window-step', 'left'); + } + } + + handleMoveRight() { + console.log('Move Right clicked'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('move-window-step', 'right'); + } + } + + async handlePersonalize() { + console.log('Personalize clicked'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + try { + await ipcRenderer.invoke('open-login-page'); + } catch (error) { + console.error('Failed to open personalize page:', error); + } + } + } + + async handleToggleInvisibility() { + console.log('Toggle Invisibility clicked'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + this.isContentProtectionOn = await ipcRenderer.invoke('toggle-content-protection'); + this.requestUpdate(); + } + } + + async handleSaveApiKey() { + const input = this.shadowRoot.getElementById('api-key-input'); + if (!input || !input.value) return; + + const newApiKey = input.value; + if (window.require) { + const { ipcRenderer } = window.require('electron'); + try { + const result = await ipcRenderer.invoke('settings:saveApiKey', newApiKey); + if (result.success) { + console.log('API Key saved successfully via IPC.'); + this.apiKey = newApiKey; + this.requestUpdate(); + } else { + console.error('Failed to save API Key via IPC:', result.error); + } + } catch(e) { + console.error('Error invoking save-api-key IPC:', e); + } + } + } + + async handleClearApiKey() { + console.log('Clear API Key clicked'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + await ipcRenderer.invoke('settings:removeApiKey'); + this.apiKey = null; + this.requestUpdate(); + } + } + + handleQuit() { + console.log('Quit clicked'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('quit-application'); + } + } + + handleFirebaseLogout() { + console.log('Firebase Logout clicked'); + if (window.require) { + const { ipcRenderer } = window.require('electron'); + ipcRenderer.invoke('firebase-logout'); + } + } + + + //////// before_modelStateService //////// + // render() { + // if (this.isLoading) { + // return html` + //
+ //
+ //
+ // Loading... + //
+ //
+ // `; + // } + + // const loggedIn = !!this.firebaseUser; + + // return html` + //
+ //
+ //
+ //

Pickle Glass

+ // + //
+ //
+ // + // + // + //
+ //
+ + //
+ // + // + //
+ + //
+ // ${this.getMainShortcuts().map(shortcut => html` + //
+ // ${shortcut.name} + //
+ // ⌘ + // ${shortcut.key} + //
+ //
+ // `)} + //
+ + // + //
+ //
+ // + // My Presets + // (${this.presets.filter(p => p.is_default === 0).length}) + // + // + // ${this.showPresets ? 'β–Ό' : 'β–Ά'} + // + //
+ + //
+ // ${this.presets.filter(p => p.is_default === 0).length === 0 ? html` + //
+ // No custom presets yet.
+ // + // Create your first preset + // + //
+ // ` : this.presets.filter(p => p.is_default === 0).map(preset => html` + //
this.handlePresetSelect(preset)}> + // ${preset.title} + // ${this.selectedPreset?.id === preset.id ? html`Selected` : ''} + //
+ // `)} + //
+ //
+ + //
+ // + + //
+ // + // + //
+ + // + + //
+ // ${this.firebaseUser + // ? html` + // + // ` + // : html` + // + // ` + // } + // + //
+ //
+ //
+ // `; + // } + //////// before_modelStateService //////// + + //////// after_modelStateService //////// + render() { + if (this.isLoading) { + return html` +
+
+
+ Loading... +
+
+ `; + } + + const loggedIn = !!this.firebaseUser; + + const apiKeyManagementHTML = html` +
+ ${Object.entries(this.providerConfig) + .filter(([id, config]) => !id.includes('-glass')) + .map(([id, config]) => html` +
+ + +
+ + +
+
+ `)} +
+ `; + + const getModelName = (type, id) => { + const models = type === 'llm' ? this.availableLlmModels : this.availableSttModels; + const model = models.find(m => m.id === id); + return model ? model.name : id; + } + + const modelSelectionHTML = html` +
+
+ + + ${this.isLlmListVisible ? html` +
+ ${this.availableLlmModels.map(model => html` +
this.selectModel('llm', model.id)}> + ${model.name} +
+ `)} +
+ ` : ''} +
+
+ + + ${this.isSttListVisible ? html` +
+ ${this.availableSttModels.map(model => html` +
this.selectModel('stt', model.id)}> + ${model.name} +
+ `)} +
+ ` : ''} +
+
+ `; + + return html` +
+
+
+

Pickle Glass

+ +
+
+ + + +
+
+ + ${apiKeyManagementHTML} + ${modelSelectionHTML} + +
+ +
+ + +
+ ${this.getMainShortcuts().map(shortcut => html` +
+ ${shortcut.name} +
+ ${this.renderShortcutKeys(shortcut.accelerator)} +
+
+ `)} +
+ +
+
+ + My Presets + (${this.presets.filter(p => p.is_default === 0).length}) + + + ${this.showPresets ? 'β–Ό' : 'β–Ά'} + +
+ +
+ ${this.presets.filter(p => p.is_default === 0).length === 0 ? html` +
+ No custom presets yet.
+ + Create your first preset + +
+ ` : this.presets.filter(p => p.is_default === 0).map(preset => html` +
this.handlePresetSelect(preset)}> + ${preset.title} + ${this.selectedPreset?.id === preset.id ? html`Selected` : ''} +
+ `)} +
+
+ +
+ + +
+ + +
+ + + +
+ ${this.firebaseUser + ? html` + + ` + : html` + + ` + } + +
+
+
+ `; + } + //////// after_modelStateService //////// +} + +customElements.define('settings-view', SettingsView); \ No newline at end of file diff --git a/src/features/settings/ShortCutSettingsView.js b/src/features/settings/ShortCutSettingsView.js new file mode 100644 index 0000000..aac45e4 --- /dev/null +++ b/src/features/settings/ShortCutSettingsView.js @@ -0,0 +1,235 @@ +import { html, css, LitElement } from '../../assets/lit-core-2.7.4.min.js'; + +const commonSystemShortcuts = new Set([ + 'Cmd+Q', 'Cmd+W', 'Cmd+A', 'Cmd+S', 'Cmd+Z', 'Cmd+X', 'Cmd+C', 'Cmd+V', 'Cmd+P', 'Cmd+F', 'Cmd+G', 'Cmd+H', 'Cmd+M', 'Cmd+N', 'Cmd+O', 'Cmd+T', + 'Ctrl+Q', 'Ctrl+W', 'Ctrl+A', 'Ctrl+S', 'Ctrl+Z', 'Ctrl+X', 'Ctrl+C', 'Ctrl+V', 'Ctrl+P', 'Ctrl+F', 'Ctrl+G', 'Ctrl+H', 'Ctrl+M', 'Ctrl+N', 'Ctrl+O', 'Ctrl+T' +]); + +const displayNameMap = { + nextStep: 'Ask Anything', + moveUp: 'Move Up Window', + moveDown: 'Move Down Window', + scrollUp: 'Scroll Up Response', + scrollDown: 'Scroll Down Response', + }; + +export class ShortcutSettingsView extends LitElement { + static styles = css` + * { font-family:'Helvetica Neue',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; + cursor:default; user-select:none; box-sizing:border-box; } + + :host { display:flex; width:100%; height:100%; color:white; } + + .container { display:flex; flex-direction:column; height:100%; + background:rgba(20,20,20,.9); border-radius:12px; + outline:.5px rgba(255,255,255,.2) solid; outline-offset:-1px; + position:relative; overflow:hidden; padding:12px; } + + .close-button{position:absolute;top:10px;right:10px;inline-size:14px;block-size:14px; + background:rgba(255,255,255,.1);border:none;border-radius:3px; + color:rgba(255,255,255,.7);display:grid;place-items:center; + font-size:14px;line-height:0;cursor:pointer;transition:.15s;z-index:10;} + .close-button:hover{background:rgba(255,255,255,.2);color:rgba(255,255,255,.9);} + + .title{font-size:14px;font-weight:500;margin:0 0 8px;padding-bottom:8px; + border-bottom:1px solid rgba(255,255,255,.1);text-align:center;} + + .scroll-area{flex:1 1 auto;overflow-y:auto;margin:0 -4px;padding:4px;} + + .shortcut-entry{display:flex;align-items:center;width:100%;gap:8px; + margin-bottom:8px;font-size:12px;padding:4px;} + .shortcut-name{flex:1 1 auto;color:rgba(255,255,255,.9);font-weight:300; + white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} + + .action-btn{background:none;border:none;color:rgba(0,122,255,.8); + font-size:11px;padding:0 4px;cursor:pointer;transition:.15s;} + .action-btn:hover{color:#0a84ff;text-decoration:underline;} + + .shortcut-input{inline-size:120px;background:rgba(0,0,0,.2); + border:1px solid rgba(255,255,255,.2);border-radius:4px; + padding:4px 6px;font:11px 'SF Mono','Menlo',monospace; + color:white;text-align:right;cursor:text;margin-left:auto;} + .shortcut-input:focus,.shortcut-input.capturing{ + outline:none;border-color:rgba(0,122,255,.6); + box-shadow:0 0 0 1px rgba(0,122,255,.3);} + + .feedback{font-size:10px;margin-top:2px;min-height:12px;} + .feedback.error{color:#ef4444;} + .feedback.success{color:#22c55e;} + + .actions{display:flex;gap:4px;padding-top:8px;border-top:1px solid rgba(255,255,255,.1);} + .settings-button{flex:1;background:rgba(255,255,255,.1); + border:1px solid rgba(255,255,255,.2);border-radius:4px; + color:white;padding:5px 10px;font-size:11px;cursor:pointer;transition:.15s;} + .settings-button:hover{background:rgba(255,255,255,.15);} + .settings-button.primary{background:rgba(0,122,255,.25);border-color:rgba(0,122,255,.6);} + .settings-button.primary:hover{background:rgba(0,122,255,.35);} + .settings-button.danger{background:rgba(255,59,48,.1);border-color:rgba(255,59,48,.3); + color:rgba(255,59,48,.9);} + .settings-button.danger:hover{background:rgba(255,59,48,.15);} + `; + + static properties = { + shortcuts: { type: Object, state: true }, + isLoading: { type: Boolean, state: true }, + capturingKey: { type: String, state: true }, + feedback: { type:Object, state:true } + }; + + constructor() { + super(); + this.shortcuts = {}; + this.feedback = {}; + this.isLoading = true; + this.capturingKey = null; + this.ipcRenderer = window.require ? window.require('electron').ipcRenderer : null; + } + + connectedCallback() { + super.connectedCallback(); + if (!this.ipcRenderer) return; + this.loadShortcutsHandler = (event, keybinds) => { + this.shortcuts = keybinds; + this.isLoading = false; + }; + this.ipcRenderer.on('load-shortcuts', this.loadShortcutsHandler); + } + + disconnectedCallback() { + super.disconnectedCallback(); + if (this.ipcRenderer && this.loadShortcutsHandler) { + this.ipcRenderer.removeListener('load-shortcuts', this.loadShortcutsHandler); + } + } + + handleKeydown(e, shortcutKey){ + e.preventDefault(); e.stopPropagation(); + const result = this._parseAccelerator(e); + if(!result) return; // modifierν‚€λ§Œ λˆ„λ₯Έ μƒνƒœ + + const {accel, error} = result; + if(error){ + this.feedback = {...this.feedback, [shortcutKey]:{type:'error',msg:error}}; + return; + } + // 성곡 + this.shortcuts = {...this.shortcuts, [shortcutKey]:accel}; + this.feedback = {...this.feedback, [shortcutKey]:{type:'success',msg:'Shortcut set'}}; + this.stopCapture(); + } + + _parseAccelerator(e){ + /* returns {accel?, error?} */ + const parts=[]; if(e.metaKey) parts.push('Cmd'); + if(e.ctrlKey) parts.push('Ctrl'); + if(e.altKey) parts.push('Alt'); + if(e.shiftKey) parts.push('Shift'); + + const isModifier=['Meta','Control','Alt','Shift'].includes(e.key); + if(isModifier) return null; + + const map={ArrowUp:'Up',ArrowDown:'Down',ArrowLeft:'Left',ArrowRight:'Right',' ':'Space'}; + parts.push(e.key.length===1? e.key.toUpperCase() : (map[e.key]||e.key)); + const accel=parts.join('+'); + + /* ---- validation ---- */ + if(parts.length===1) return {error:'Invalid shortcut: needs a modifier'}; + if(parts.length>4) return {error:'Invalid shortcut: max 4 keys'}; + if(commonSystemShortcuts.has(accel)) return {error:'Invalid shortcut: system reserved'}; + return {accel}; + } + + startCapture(key){ this.capturingKey = key; this.feedback = {...this.feedback, [key]:undefined}; } + + disableShortcut(key){ + this.shortcuts = {...this.shortcuts, [key]:''}; // 곡백 => μž‘λ™ X + this.feedback = {...this.feedback, [key]:{type:'success',msg:'Shortcut disabled'}}; + } + + stopCapture() { + this.capturingKey = null; + } + + async handleSave() { + if (!this.ipcRenderer) return; + const result = await this.ipcRenderer.invoke('save-shortcuts', this.shortcuts); + if (!result.success) { + alert('Failed to save shortcuts: ' + result.error); + } + } + + handleClose() { + if (!this.ipcRenderer) return; + this.ipcRenderer.send('close-shortcut-editor'); + } + + async handleResetToDefault() { + if (!this.ipcRenderer) return; + const confirmation = confirm("Are you sure you want to reset all shortcuts to their default values?"); + if (!confirmation) return; + + try { + const defaultShortcuts = await this.ipcRenderer.invoke('get-default-shortcuts'); + this.shortcuts = defaultShortcuts; + } catch (error) { + alert('Failed to load default settings.'); + } + } + + formatShortcutName(name) { + if (displayNameMap[name]) { + return displayNameMap[name]; + } + const result = name.replace(/([A-Z])/g, " $1"); + return result.charAt(0).toUpperCase() + result.slice(1); + } + + render(){ + if(this.isLoading){ + return html`
Loading Shortcuts...
`; + } + return html` +
+ +

Edit Shortcuts

+ +
+ ${Object.keys(this.shortcuts).map(key=>html` +
+
+ ${this.formatShortcutName(key)} + + + + + + this.startCapture(key)} + @keydown=${e=>this.handleKeydown(e,key)} + @blur=${()=>this.stopCapture()} + /> +
+ + ${this.feedback[key] ? html` + ` : html`` + } +
+ `)} +
+ +
+ + + +
+
+ `; + } + } + +customElements.define('shortcut-settings-view', ShortcutSettingsView); \ No newline at end of file diff --git a/src/features/settings/repositories/index.js b/src/features/settings/repositories/index.js new file mode 100644 index 0000000..508ebe5 --- /dev/null +++ b/src/features/settings/repositories/index.js @@ -0,0 +1,21 @@ +const sqliteRepository = require('./sqlite.repository'); +// const firebaseRepository = require('./firebase.repository'); // Future implementation +const authService = require('../../../common/services/authService'); + +function getRepository() { + // In the future, we can check the user's login status from authService + // const user = authService.getCurrentUser(); + // if (user.isLoggedIn) { + // return firebaseRepository; + // } + return sqliteRepository; +} + +// Directly export functions for ease of use, decided by the strategy +module.exports = { + getPresets: (...args) => getRepository().getPresets(...args), + getPresetTemplates: (...args) => getRepository().getPresetTemplates(...args), + createPreset: (...args) => getRepository().createPreset(...args), + updatePreset: (...args) => getRepository().updatePreset(...args), + deletePreset: (...args) => getRepository().deletePreset(...args), +}; \ No newline at end of file diff --git a/src/features/settings/repositories/sqlite.repository.js b/src/features/settings/repositories/sqlite.repository.js new file mode 100644 index 0000000..6611346 --- /dev/null +++ b/src/features/settings/repositories/sqlite.repository.js @@ -0,0 +1,99 @@ +const sqliteClient = require('../../../common/services/sqliteClient'); + +function getPresets(uid) { + const db = sqliteClient.getDb(); + const query = ` + SELECT * FROM prompt_presets + WHERE uid = ? OR is_default = 1 + ORDER BY is_default DESC, title ASC + `; + + try { + return db.prepare(query).all(uid) || []; + } catch (err) { + console.error('SQLite: Failed to get presets:', err); + throw err; + } +} + +function getPresetTemplates() { + const db = sqliteClient.getDb(); + const query = ` + SELECT * FROM prompt_presets + WHERE is_default = 1 + ORDER BY title ASC + `; + + try { + return db.prepare(query).all() || []; + } catch (err) { + console.error('SQLite: Failed to get preset templates:', err); + throw err; + } +} + +function createPreset({ uid, title, prompt }) { + const db = sqliteClient.getDb(); + const id = require('crypto').randomUUID(); + const now = Math.floor(Date.now() / 1000); + const query = ` + INSERT INTO prompt_presets (id, uid, title, prompt, is_default, created_at, sync_state) + VALUES (?, ?, ?, ?, 0, ?, 'dirty') + `; + + try { + db.prepare(query).run(id, uid, title, prompt, now); + return { id }; + } catch (err) { + console.error('SQLite: Failed to create preset:', err); + throw err; + } +} + +function updatePreset(id, { title, prompt }, uid) { + const db = sqliteClient.getDb(); + const now = Math.floor(Date.now() / 1000); + const query = ` + UPDATE prompt_presets + SET title = ?, prompt = ?, sync_state = 'dirty', updated_at = ? + WHERE id = ? AND uid = ? AND is_default = 0 + `; + + try { + const result = db.prepare(query).run(title, prompt, now, id, uid); + if (result.changes === 0) { + throw new Error('Preset not found, is default, or permission denied'); + } + return { changes: result.changes }; + } catch (err) { + console.error('SQLite: Failed to update preset:', err); + throw err; + } +} + +function deletePreset(id, uid) { + const db = sqliteClient.getDb(); + const query = ` + DELETE FROM prompt_presets + WHERE id = ? AND uid = ? AND is_default = 0 + `; + + try { + const result = db.prepare(query).run(id, uid); + if (result.changes === 0) { + throw new Error('Preset not found, is default, or permission denied'); + } + return { changes: result.changes }; + } catch (err) { + console.error('SQLite: Failed to delete preset:', err); + throw err; + } +} + +module.exports = { + getPresets, + getPresetTemplates, + createPreset, + updatePreset, + deletePreset +}; \ No newline at end of file diff --git a/src/features/settings/settingsService.js b/src/features/settings/settingsService.js new file mode 100644 index 0000000..6360f36 --- /dev/null +++ b/src/features/settings/settingsService.js @@ -0,0 +1,462 @@ +const { ipcMain, BrowserWindow } = require('electron'); +const Store = require('electron-store'); +const authService = require('../../common/services/authService'); +const userRepository = require('../../common/repositories/user'); +const settingsRepository = require('./repositories'); +const { getStoredApiKey, getStoredProvider, windowPool } = require('../../electron/windowManager'); + +const store = new Store({ + name: 'pickle-glass-settings', + defaults: { + users: {} + } +}); + +// Configuration constants +const NOTIFICATION_CONFIG = { + RELEVANT_WINDOW_TYPES: ['settings', 'main'], + DEBOUNCE_DELAY: 300, // prevent spam during bulk operations (ms) + MAX_RETRY_ATTEMPTS: 3, + RETRY_BASE_DELAY: 1000, // exponential backoff base (ms) +}; + +// window targeting system +class WindowNotificationManager { + constructor() { + this.pendingNotifications = new Map(); + } + + /** + * Send notifications only to relevant windows + * @param {string} event - Event name + * @param {*} data - Event data + * @param {object} options - Notification options + */ + notifyRelevantWindows(event, data = null, options = {}) { + const { + windowTypes = NOTIFICATION_CONFIG.RELEVANT_WINDOW_TYPES, + debounce = NOTIFICATION_CONFIG.DEBOUNCE_DELAY + } = options; + + if (debounce > 0) { + this.debounceNotification(event, () => { + this.sendToTargetWindows(event, data, windowTypes); + }, debounce); + } else { + this.sendToTargetWindows(event, data, windowTypes); + } + } + + sendToTargetWindows(event, data, windowTypes) { + const relevantWindows = this.getRelevantWindows(windowTypes); + + if (relevantWindows.length === 0) { + console.log(`[WindowNotificationManager] No relevant windows found for event: ${event}`); + return; + } + + console.log(`[WindowNotificationManager] Sending ${event} to ${relevantWindows.length} relevant windows`); + + relevantWindows.forEach(win => { + try { + if (data) { + win.webContents.send(event, data); + } else { + win.webContents.send(event); + } + } catch (error) { + console.warn(`[WindowNotificationManager] Failed to send ${event} to window:`, error.message); + } + }); + } + + getRelevantWindows(windowTypes) { + const allWindows = BrowserWindow.getAllWindows(); + const relevantWindows = []; + + allWindows.forEach(win => { + if (win.isDestroyed()) return; + + for (const [windowName, poolWindow] of windowPool || []) { + if (poolWindow === win && windowTypes.includes(windowName)) { + if (windowName === 'settings' || win.isVisible()) { + relevantWindows.push(win); + } + break; + } + } + }); + + return relevantWindows; + } + + debounceNotification(key, fn, delay) { + // Clear existing timeout + if (this.pendingNotifications.has(key)) { + clearTimeout(this.pendingNotifications.get(key)); + } + + // Set new timeout + const timeoutId = setTimeout(() => { + fn(); + this.pendingNotifications.delete(key); + }, delay); + + this.pendingNotifications.set(key, timeoutId); + } + + cleanup() { + // Clear all pending notifications + this.pendingNotifications.forEach(timeoutId => clearTimeout(timeoutId)); + this.pendingNotifications.clear(); + } +} + +// Global instance +const windowNotificationManager = new WindowNotificationManager(); + +// Default keybinds configuration +const DEFAULT_KEYBINDS = { + mac: { + moveUp: 'Cmd+Up', + moveDown: 'Cmd+Down', + moveLeft: 'Cmd+Left', + moveRight: 'Cmd+Right', + toggleVisibility: 'Cmd+\\', + toggleClickThrough: 'Cmd+M', + nextStep: 'Cmd+Enter', + manualScreenshot: 'Cmd+Shift+S', + previousResponse: 'Cmd+[', + nextResponse: 'Cmd+]', + scrollUp: 'Cmd+Shift+Up', + scrollDown: 'Cmd+Shift+Down', + }, + windows: { + moveUp: 'Ctrl+Up', + moveDown: 'Ctrl+Down', + moveLeft: 'Ctrl+Left', + moveRight: 'Ctrl+Right', + toggleVisibility: 'Ctrl+\\', + toggleClickThrough: 'Ctrl+M', + nextStep: 'Ctrl+Enter', + manualScreenshot: 'Ctrl+Shift+S', + previousResponse: 'Ctrl+[', + nextResponse: 'Ctrl+]', + scrollUp: 'Ctrl+Shift+Up', + scrollDown: 'Ctrl+Shift+Down', + } +}; + +// Service state +let currentSettings = null; + +function getDefaultSettings() { + const isMac = process.platform === 'darwin'; + return { + profile: 'school', + language: 'en', + screenshotInterval: '5000', + imageQuality: '0.8', + layoutMode: 'stacked', + keybinds: isMac ? DEFAULT_KEYBINDS.mac : DEFAULT_KEYBINDS.windows, + throttleTokens: 500, + maxTokens: 2000, + throttlePercent: 80, + googleSearchEnabled: false, + backgroundTransparency: 0.5, + fontSize: 14, + contentProtection: true + }; +} + +async function getSettings() { + try { + const uid = authService.getCurrentUserId(); + const userSettingsKey = uid ? `users.${uid}` : 'users.default'; + + const defaultSettings = getDefaultSettings(); + const savedSettings = store.get(userSettingsKey, {}); + + currentSettings = { ...defaultSettings, ...savedSettings }; + return currentSettings; + } catch (error) { + console.error('[SettingsService] Error getting settings from store:', error); + return getDefaultSettings(); + } +} + +async function saveSettings(settings) { + try { + const uid = authService.getCurrentUserId(); + const userSettingsKey = uid ? `users.${uid}` : 'users.default'; + + const currentSaved = store.get(userSettingsKey, {}); + const newSettings = { ...currentSaved, ...settings }; + + store.set(userSettingsKey, newSettings); + currentSettings = newSettings; + + // Use smart notification system + windowNotificationManager.notifyRelevantWindows('settings-updated', currentSettings); + + return { success: true }; + } catch (error) { + console.error('[SettingsService] Error saving settings to store:', error); + return { success: false, error: error.message }; + } +} + +async function getPresets() { + try { + const uid = authService.getCurrentUserId(); + if (!uid) { + // Logged out users only see default presets + return await settingsRepository.getPresetTemplates(); + } + + const presets = await settingsRepository.getPresets(uid); + return presets; + } catch (error) { + console.error('[SettingsService] Error getting presets:', error); + return []; + } +} + +async function getPresetTemplates() { + try { + const templates = await settingsRepository.getPresetTemplates(); + return templates; + } catch (error) { + console.error('[SettingsService] Error getting preset templates:', error); + return []; + } +} + +async function createPreset(title, prompt) { + try { + const uid = authService.getCurrentUserId(); + if (!uid) { + throw new Error("User not logged in, cannot create preset."); + } + + const result = await settingsRepository.createPreset({ uid, title, prompt }); + + windowNotificationManager.notifyRelevantWindows('presets-updated', { + action: 'created', + presetId: result.id, + title + }); + + return { success: true, id: result.id }; + } catch (error) { + console.error('[SettingsService] Error creating preset:', error); + return { success: false, error: error.message }; + } +} + +async function updatePreset(id, title, prompt) { + try { + const uid = authService.getCurrentUserId(); + if (!uid) { + throw new Error("User not logged in, cannot update preset."); + } + + await settingsRepository.updatePreset(id, { title, prompt }, uid); + + windowNotificationManager.notifyRelevantWindows('presets-updated', { + action: 'updated', + presetId: id, + title + }); + + return { success: true }; + } catch (error) { + console.error('[SettingsService] Error updating preset:', error); + return { success: false, error: error.message }; + } +} + +async function deletePreset(id) { + try { + const uid = authService.getCurrentUserId(); + if (!uid) { + throw new Error("User not logged in, cannot delete preset."); + } + + await settingsRepository.deletePreset(id, uid); + + windowNotificationManager.notifyRelevantWindows('presets-updated', { + action: 'deleted', + presetId: id + }); + + return { success: true }; + } catch (error) { + console.error('[SettingsService] Error deleting preset:', error); + return { success: false, error: error.message }; + } +} + +async function saveApiKey(apiKey, provider = 'openai') { + try { + const uid = authService.getCurrentUserId(); + if (!uid) { + // For non-logged-in users, save to local storage + const { app } = require('electron'); + const Store = require('electron-store'); + const store = new Store(); + store.set('apiKey', apiKey); + store.set('provider', provider); + + // Notify windows + BrowserWindow.getAllWindows().forEach(win => { + if (!win.isDestroyed()) { + win.webContents.send('api-key-validated', apiKey); + } + }); + + return { success: true }; + } + + // For logged-in users, save to database + await userRepository.saveApiKey(apiKey, uid, provider); + + // Notify windows + BrowserWindow.getAllWindows().forEach(win => { + if (!win.isDestroyed()) { + win.webContents.send('api-key-validated', apiKey); + } + }); + + return { success: true }; + } catch (error) { + console.error('[SettingsService] Error saving API key:', error); + return { success: false, error: error.message }; + } +} + +async function removeApiKey() { + try { + const uid = authService.getCurrentUserId(); + if (!uid) { + // For non-logged-in users, remove from local storage + const { app } = require('electron'); + const Store = require('electron-store'); + const store = new Store(); + store.delete('apiKey'); + store.delete('provider'); + } else { + // For logged-in users, remove from database + await userRepository.saveApiKey(null, uid, null); + } + + // Notify windows + BrowserWindow.getAllWindows().forEach(win => { + if (!win.isDestroyed()) { + win.webContents.send('api-key-removed'); + } + }); + + return { success: true }; + } catch (error) { + console.error('[SettingsService] Error removing API key:', error); + return { success: false, error: error.message }; + } +} + +async function updateContentProtection(enabled) { + try { + const settings = await getSettings(); + settings.contentProtection = enabled; + + // Update content protection in main window + const { app } = require('electron'); + const mainWindow = windowPool.get('main'); + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.setContentProtection(enabled); + } + + return await saveSettings(settings); + } catch (error) { + console.error('[SettingsService] Error updating content protection:', error); + return { success: false, error: error.message }; + } +} + +function initialize() { + // cleanup + windowNotificationManager.cleanup(); + + // IPC handlers for settings + ipcMain.handle('settings:getSettings', async () => { + return await getSettings(); + }); + + ipcMain.handle('settings:saveSettings', async (event, settings) => { + return await saveSettings(settings); + }); + + // IPC handlers for presets + ipcMain.handle('settings:getPresets', async () => { + return await getPresets(); + }); + + ipcMain.handle('settings:getPresetTemplates', async () => { + return await getPresetTemplates(); + }); + + ipcMain.handle('settings:createPreset', async (event, title, prompt) => { + return await createPreset(title, prompt); + }); + + ipcMain.handle('settings:updatePreset', async (event, id, title, prompt) => { + return await updatePreset(id, title, prompt); + }); + + ipcMain.handle('settings:deletePreset', async (event, id) => { + return await deletePreset(id); + }); + + ipcMain.handle('settings:saveApiKey', async (event, apiKey, provider) => { + return await saveApiKey(apiKey, provider); + }); + + ipcMain.handle('settings:removeApiKey', async () => { + return await removeApiKey(); + }); + + ipcMain.handle('settings:updateContentProtection', async (event, enabled) => { + return await updateContentProtection(enabled); + }); + + console.log('[SettingsService] Initialized and ready.'); +} + +// Cleanup function +function cleanup() { + windowNotificationManager.cleanup(); + console.log('[SettingsService] Cleaned up resources.'); +} + +function notifyPresetUpdate(action, presetId, title = null) { + const data = { action, presetId }; + if (title) data.title = title; + + windowNotificationManager.notifyRelevantWindows('presets-updated', data); +} + +module.exports = { + initialize, + cleanup, + notifyPresetUpdate, + getSettings, + saveSettings, + getPresets, + getPresetTemplates, + createPreset, + updatePreset, + deletePreset, + saveApiKey, + removeApiKey, + updateContentProtection, +}; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 2db9968..31ae822 100644 --- a/src/index.js +++ b/src/index.js @@ -11,153 +11,247 @@ if (require('electron-squirrel-startup')) { process.exit(0); } -const { app, BrowserWindow, shell, ipcMain, dialog } = require('electron'); +const { app, BrowserWindow, shell, ipcMain, dialog, desktopCapturer, session } = require('electron'); const { createWindows } = require('./electron/windowManager.js'); -const { setupLiveSummaryIpcHandlers, stopMacOSAudioCapture } = require('./features/listen/liveSummaryService.js'); +const ListenService = require('./features/listen/listenService'); +const { initializeFirebase } = require('./common/services/firebaseClient'); const databaseInitializer = require('./common/services/databaseInitializer'); -const dataService = require('./common/services/dataService'); +const authService = require('./common/services/authService'); const path = require('node:path'); -const { Deeplink } = require('electron-deeplink'); const express = require('express'); const fetch = require('node-fetch'); const { autoUpdater } = require('electron-updater'); +const { EventEmitter } = require('events'); +const askService = require('./features/ask/askService'); +const settingsService = require('./features/settings/settingsService'); +const sessionRepository = require('./common/repositories/session'); +const ModelStateService = require('./common/services/modelStateService'); +const eventBridge = new EventEmitter(); let WEB_PORT = 3000; -const openaiSessionRef = { current: null }; -let deeplink = null; // Initialize as null -let pendingDeepLinkUrl = null; // Store any deep link that arrives before initialization +const listenService = new ListenService(); +// Make listenService globally accessible so other modules (e.g., windowManager, askService) can reuse the same instance +global.listenService = listenService; -function createMainWindows() { - createWindows(); +//////// after_modelStateService //////// +const modelStateService = new ModelStateService(authService); +global.modelStateService = modelStateService; +//////// after_modelStateService //////// - const { windowPool } = require('./electron/windowManager'); - const headerWindow = windowPool.get('header'); - - // Initialize deeplink after windows are created - if (!deeplink && headerWindow) { - try { - deeplink = new Deeplink({ - app, - mainWindow: headerWindow, - protocol: 'pickleglass', - isDev: !app.isPackaged, - debugLogging: true - }); - - deeplink.on('received', (url) => { - console.log('[deeplink] received:', url); - handleCustomUrl(url); - }); - - console.log('[deeplink] Initialized with main window'); - - // Handle any pending deep link - if (pendingDeepLinkUrl) { - console.log('[deeplink] Processing pending deep link:', pendingDeepLinkUrl); - handleCustomUrl(pendingDeepLinkUrl); - pendingDeepLinkUrl = null; +// Native deep link handling - cross-platform compatible +let pendingDeepLinkUrl = null; + +function setupProtocolHandling() { + // Protocol registration - must be done before app is ready + try { + if (!app.isDefaultProtocolClient('pickleglass')) { + const success = app.setAsDefaultProtocolClient('pickleglass'); + if (success) { + console.log('[Protocol] Successfully set as default protocol client for pickleglass://'); + } else { + console.warn('[Protocol] Failed to set as default protocol client - this may affect deep linking'); } - } catch (error) { - console.error('[deeplink] Failed to initialize deep link:', error); - deeplink = null; + } else { + console.log('[Protocol] Already registered as default protocol client for pickleglass://'); } + } catch (error) { + console.error('[Protocol] Error during protocol registration:', error); } -} -app.whenReady().then(async () => { - const gotTheLock = app.requestSingleInstanceLock(); - if (!gotTheLock) { - app.quit(); - return; - } else { - app.on('second-instance', (event, commandLine, workingDirectory) => { - const { windowPool } = require('./electron/windowManager'); - if (windowPool) { - const header = windowPool.get('header'); - if (header) { - if (header.isMinimized()) header.restore(); - header.focus(); - return; + // Handle protocol URLs on Windows/Linux + app.on('second-instance', (event, commandLine, workingDirectory) => { + console.log('[Protocol] Second instance command line:', commandLine); + + focusMainWindow(); + + let protocolUrl = null; + + // Search through all command line arguments for a valid protocol URL + for (const arg of commandLine) { + if (arg && typeof arg === 'string' && arg.startsWith('pickleglass://')) { + // Clean up the URL by removing problematic characters + const cleanUrl = arg.replace(/[\\β‚©]/g, ''); + + // Additional validation for Windows + if (process.platform === 'win32') { + // On Windows, ensure the URL doesn't contain file path indicators + if (!cleanUrl.includes(':') || cleanUrl.indexOf('://') === cleanUrl.lastIndexOf(':')) { + protocolUrl = cleanUrl; + break; + } + } else { + protocolUrl = cleanUrl; + break; } } - - const windows = BrowserWindow.getAllWindows(); - if (windows.length > 0) { - const mainWindow = windows[0]; - if (mainWindow.isMinimized()) mainWindow.restore(); - mainWindow.focus(); - } - }); - } + } + + if (protocolUrl) { + console.log('[Protocol] Valid URL found from second instance:', protocolUrl); + handleCustomUrl(protocolUrl); + } else { + console.log('[Protocol] No valid protocol URL found in command line arguments'); + console.log('[Protocol] Command line args:', commandLine); + } + }); - const dbInitSuccess = await databaseInitializer.initialize(); - if (!dbInitSuccess) { - console.error('>>> [index.js] Database initialization failed - some features may not work'); - } else { - console.log('>>> [index.js] Database initialized successfully'); - } + // Handle protocol URLs on macOS + app.on('open-url', (event, url) => { + event.preventDefault(); + console.log('[Protocol] Received URL via open-url:', url); + + if (!url || !url.startsWith('pickleglass://')) { + console.warn('[Protocol] Invalid URL format:', url); + return; + } - WEB_PORT = await startWebStack(); - console.log('Web front-end listening on', WEB_PORT); + if (app.isReady()) { + handleCustomUrl(url); + } else { + pendingDeepLinkUrl = url; + console.log('[Protocol] App not ready, storing URL for later'); + } + }); +} + +function focusMainWindow() { + const { windowPool } = require('./electron/windowManager'); + if (windowPool) { + const header = windowPool.get('header'); + if (header && !header.isDestroyed()) { + if (header.isMinimized()) header.restore(); + header.focus(); + return true; + } + } - setupLiveSummaryIpcHandlers(openaiSessionRef); - setupGeneralIpcHandlers(); + // Fallback: focus any available window + const windows = BrowserWindow.getAllWindows(); + if (windows.length > 0) { + const mainWindow = windows[0]; + if (!mainWindow.isDestroyed()) { + if (mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.focus(); + return true; + } + } + + return false; +} - createMainWindows(); +if (process.platform === 'win32') { + for (const arg of process.argv) { + if (arg && typeof arg === 'string' && arg.startsWith('pickleglass://')) { + // Clean up the URL by removing problematic characters (korean characters issue...) + const cleanUrl = arg.replace(/[\\β‚©]/g, ''); + + if (!cleanUrl.includes(':') || cleanUrl.indexOf('://') === cleanUrl.lastIndexOf(':')) { + console.log('[Protocol] Found protocol URL in initial arguments:', cleanUrl); + pendingDeepLinkUrl = cleanUrl; + break; + } + } + } + + console.log('[Protocol] Initial process.argv:', process.argv); +} + +const gotTheLock = app.requestSingleInstanceLock(); +if (!gotTheLock) { + app.quit(); + process.exit(0); +} + +// setup protocol after single instance lock +setupProtocolHandling(); + +app.whenReady().then(async () => { + + // Setup native loopback audio capture for Windows + session.defaultSession.setDisplayMediaRequestHandler((request, callback) => { + desktopCapturer.getSources({ types: ['screen'] }).then((sources) => { + // Grant access to the first screen found with loopback audio + callback({ video: sources[0], audio: 'loopback' }); + }).catch((error) => { + console.error('Failed to get desktop capturer sources:', error); + callback({}); + }); + }); + + // Initialize core services + initializeFirebase(); + + try { + await databaseInitializer.initialize(); + console.log('>>> [index.js] Database initialized successfully'); + + // Clean up zombie sessions from previous runs first + sessionRepository.endAllActiveSessions(); + + authService.initialize(); + + //////// after_modelStateService //////// + modelStateService.initialize(); + //////// after_modelStateService //////// + + listenService.setupIpcHandlers(); + askService.initialize(); + settingsService.initialize(); + setupGeneralIpcHandlers(); + + // Start web server and create windows ONLY after all initializations are successful + WEB_PORT = await startWebStack(); + console.log('Web front-end listening on', WEB_PORT); + + createWindows(); + + } catch (err) { + console.error('>>> [index.js] Database initialization failed - some features may not work', err); + // Optionally, show an error dialog to the user + dialog.showErrorBox( + 'Application Error', + 'A critical error occurred during startup. Some features might be disabled. Please restart the application.' + ); + } initAutoUpdater(); + + // Process any pending deep link after everything is initialized + if (pendingDeepLinkUrl) { + console.log('[Protocol] Processing pending URL:', pendingDeepLinkUrl); + handleCustomUrl(pendingDeepLinkUrl); + pendingDeepLinkUrl = null; + } }); app.on('window-all-closed', () => { - stopMacOSAudioCapture(); + listenService.stopMacOSAudioCapture(); if (process.platform !== 'darwin') { app.quit(); } }); -app.on('before-quit', () => { - stopMacOSAudioCapture(); +app.on('before-quit', async () => { + console.log('[Shutdown] App is about to quit.'); + listenService.stopMacOSAudioCapture(); + await sessionRepository.endAllActiveSessions(); databaseInitializer.close(); }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { - createMainWindows(); + createWindows(); } }); -// Add macOS native deep link handling as fallback -app.on('open-url', (event, url) => { - event.preventDefault(); - console.log('[app] open-url received:', url); - - if (!deeplink) { - // Store the URL if deeplink isn't ready yet - pendingDeepLinkUrl = url; - console.log('[app] Deep link stored for later processing'); - } else { - handleCustomUrl(url); - } -}); - -// Ensure app can handle the protocol -app.setAsDefaultProtocolClient('pickleglass'); - function setupGeneralIpcHandlers() { - ipcMain.handle('open-external', async (event, url) => { - try { - await shell.openExternal(url); - return { success: true }; - } catch (error) { - console.error('Error opening external URL:', error); - return { success: false, error: error.message }; - } - }); + const userRepository = require('./common/repositories/user'); + const presetRepository = require('./common/repositories/preset'); - ipcMain.handle('save-api-key', async (event, apiKey) => { + ipcMain.handle('save-api-key', (event, apiKey) => { try { - await dataService.saveApiKey(apiKey); + userRepository.saveApiKey(apiKey, authService.getCurrentUserId()); BrowserWindow.getAllWindows().forEach(win => { win.webContents.send('api-key-updated'); }); @@ -168,21 +262,12 @@ function setupGeneralIpcHandlers() { } }); - ipcMain.handle('check-api-key', async () => { - return await dataService.checkApiKey(); + ipcMain.handle('get-user-presets', () => { + return presetRepository.getPresets(authService.getCurrentUserId()); }); - ipcMain.handle('get-user-presets', async () => { - return await dataService.getUserPresets(); - }); - - ipcMain.handle('get-preset-templates', async () => { - return await dataService.getPresetTemplates(); - }); - - ipcMain.on('set-current-user', (event, uid) => { - console.log(`[IPC] set-current-user: ${uid}`); - dataService.setCurrentUser(uid); + ipcMain.handle('get-preset-templates', () => { + return presetRepository.getPresetTemplates(); }); ipcMain.handle('start-firebase-auth', async () => { @@ -197,69 +282,144 @@ function setupGeneralIpcHandlers() { } }); - ipcMain.on('firebase-auth-success', async (event, firebaseUser) => { - console.log('[IPC] firebase-auth-success:', firebaseUser.uid); - try { - await dataService.findOrCreateUser(firebaseUser); - dataService.setCurrentUser(firebaseUser.uid); - - BrowserWindow.getAllWindows().forEach(win => { - if (win !== event.sender.getOwnerBrowserWindow()) { - win.webContents.send('user-changed', firebaseUser); - } - }); - } catch (error) { - console.error('[IPC] Failed to handle firebase-auth-success:', error); - } - }); - - ipcMain.handle('get-api-url', () => { - return process.env.pickleglass_API_URL || 'http://localhost:9001'; - }); - ipcMain.handle('get-web-url', () => { return process.env.pickleglass_WEB_URL || 'http://localhost:3000'; }); - ipcMain.on('get-api-url-sync', (event) => { - event.returnValue = process.env.pickleglass_API_URL || 'http://localhost:9001'; + ipcMain.handle('get-current-user', () => { + return authService.getCurrentUser(); }); - ipcMain.handle('get-database-status', async () => { - return await databaseInitializer.getStatus(); - }); + // --- Web UI Data Handlers (New) --- + setupWebDataHandlers(); +} - ipcMain.handle('reset-database', async () => { - return await databaseInitializer.reset(); - }); +function setupWebDataHandlers() { + const sessionRepository = require('./common/repositories/session'); + const sttRepository = require('./features/listen/stt/repositories'); + const summaryRepository = require('./features/listen/summary/repositories'); + const askRepository = require('./features/ask/repositories'); + const userRepository = require('./common/repositories/user'); + const presetRepository = require('./common/repositories/preset'); - ipcMain.handle('get-current-user', async () => { + const handleRequest = (channel, responseChannel, payload) => { + let result; + const currentUserId = authService.getCurrentUserId(); try { - const user = await dataService.sqliteClient.getUser(dataService.currentUserId); - if (user) { - return { - id: user.uid, - name: user.display_name, - isAuthenticated: user.uid !== 'default_user' - }; - } - throw new Error('User not found in DataService'); - } catch (error) { - console.error('Failed to get current user via DataService:', error); - return { - id: 'default_user', - name: 'Default User', - isAuthenticated: false - }; - } - }); + switch (channel) { + // SESSION + case 'get-sessions': + result = sessionRepository.getAllByUserId(currentUserId); + break; + case 'get-session-details': + const session = sessionRepository.getById(payload); + if (!session) { + result = null; + break; + } + const transcripts = sttRepository.getAllTranscriptsBySessionId(payload); + const ai_messages = askRepository.getAllAiMessagesBySessionId(payload); + const summary = summaryRepository.getSummaryBySessionId(payload); + result = { session, transcripts, ai_messages, summary }; + break; + case 'delete-session': + result = sessionRepository.deleteWithRelatedData(payload); + break; + case 'create-session': + const id = sessionRepository.create(currentUserId, 'ask'); + if (payload.title) { + sessionRepository.updateTitle(id, payload.title); + } + result = { id }; + break; + + // USER + case 'get-user-profile': + result = userRepository.getById(currentUserId); + break; + case 'update-user-profile': + result = userRepository.update({ uid: currentUserId, ...payload }); + break; + case 'find-or-create-user': + result = userRepository.findOrCreate(payload); + break; + case 'save-api-key': + result = userRepository.saveApiKey(payload, currentUserId); + break; + case 'check-api-key-status': + const user = userRepository.getById(currentUserId); + result = { hasApiKey: !!user?.api_key && user.api_key.length > 0 }; + break; + case 'delete-account': + result = userRepository.deleteById(currentUserId); + break; + // PRESET + case 'get-presets': + result = presetRepository.getPresets(currentUserId); + break; + case 'create-preset': + result = presetRepository.create({ ...payload, uid: currentUserId }); + settingsService.notifyPresetUpdate('created', result.id, payload.title); + break; + case 'update-preset': + result = presetRepository.update(payload.id, payload.data, currentUserId); + settingsService.notifyPresetUpdate('updated', payload.id, payload.data.title); + break; + case 'delete-preset': + result = presetRepository.delete(payload, currentUserId); + settingsService.notifyPresetUpdate('deleted', payload); + break; + + // BATCH + case 'get-batch-data': + const includes = payload ? payload.split(',').map(item => item.trim()) : ['profile', 'presets', 'sessions']; + const batchResult = {}; + + if (includes.includes('profile')) { + batchResult.profile = userRepository.getById(currentUserId); + } + if (includes.includes('presets')) { + batchResult.presets = presetRepository.getPresets(currentUserId); + } + if (includes.includes('sessions')) { + batchResult.sessions = sessionRepository.getAllByUserId(currentUserId); + } + result = batchResult; + break; + + default: + throw new Error(`Unknown web data channel: ${channel}`); + } + eventBridge.emit(responseChannel, { success: true, data: result }); + } catch (error) { + console.error(`Error handling web data request for ${channel}:`, error); + eventBridge.emit(responseChannel, { success: false, error: error.message }); + } + }; + + eventBridge.on('web-data-request', handleRequest); } async function handleCustomUrl(url) { try { console.log('[Custom URL] Processing URL:', url); + // Validate and clean URL + if (!url || typeof url !== 'string' || !url.startsWith('pickleglass://')) { + console.error('[Custom URL] Invalid URL format:', url); + return; + } + + // Clean up URL by removing problematic characters + const cleanUrl = url.replace(/[\\β‚©]/g, ''); + + // Additional validation + if (cleanUrl !== url) { + console.log('[Custom URL] Cleaned URL from:', url, 'to:', cleanUrl); + url = cleanUrl; + } + const urlObj = new URL(url); const action = urlObj.hostname; const params = Object.fromEntries(urlObj.searchParams); @@ -293,18 +453,12 @@ async function handleCustomUrl(url) { } async function handleFirebaseAuthCallback(params) { + const userRepository = require('./common/repositories/user'); const { token: idToken } = params; if (!idToken) { console.error('[Auth] Firebase auth callback is missing ID token.'); - const { windowPool } = require('./electron/windowManager'); - const header = windowPool.get('header'); - if (header) { - header.webContents.send('login-successful', { - error: 'authentication_failed', - message: 'ID token not provided in deep link.' - }); - } + // No need to send IPC, the UI won't transition without a successful auth state change. return; } @@ -320,8 +474,6 @@ async function handleFirebaseAuthCallback(params) { const data = await response.json(); - - if (!response.ok || !data.success) { throw new Error(data.error || 'Failed to exchange token.'); } @@ -336,71 +488,32 @@ async function handleFirebaseAuthCallback(params) { photoURL: user.picture }; - await dataService.findOrCreateUser(firebaseUser); - dataService.setCurrentUser(user.uid); + // 1. Sync user data to local DB + userRepository.findOrCreate(firebaseUser); console.log('[Auth] User data synced with local DB.'); - // if (firebaseUser.email && idToken) { - // try { - // const { getVirtualKeyByEmail, setApiKey } = require('./electron/windowManager'); - // console.log('[Auth] Fetching virtual key for:', firebaseUser.email); - // const vKey = await getVirtualKeyByEmail(firebaseUser.email, idToken); - // console.log('[Auth] Virtual key fetched successfully'); - - // await setApiKey(vKey); - // console.log('[Auth] Virtual key saved successfully'); - - // const { setCurrentFirebaseUser } = require('./electron/windowManager'); - // setCurrentFirebaseUser(firebaseUser); - - // const { windowPool } = require('./electron/windowManager'); - // windowPool.forEach(win => { - // if (win && !win.isDestroyed()) { - // win.webContents.send('api-key-updated'); - // win.webContents.send('firebase-user-updated', firebaseUser); - // } - // }); - // } catch (error) { - // console.error('[Auth] Virtual key fetch failed:', error); - // } - // } + // 2. Sign in using the authService in the main process + await authService.signInWithCustomToken(customToken); + console.log('[Auth] Main process sign-in initiated. Waiting for onAuthStateChanged...'); + // 3. Focus the app window const { windowPool } = require('./electron/windowManager'); const header = windowPool.get('header'); - if (header) { if (header.isMinimized()) header.restore(); header.focus(); - - console.log('[Auth] Sending custom token to renderer for sign-in.'); - header.webContents.send('login-successful', { - customToken: customToken, - user: firebaseUser, - success: true - }); - - BrowserWindow.getAllWindows().forEach(win => { - if (win !== header) { - win.webContents.send('user-changed', firebaseUser); - } - }); - - console.log('[Auth] Firebase authentication completed successfully'); - } else { - console.error('[Auth] Header window not found after getting custom token.'); + console.error('[Auth] Header window not found after auth callback.'); } } catch (error) { - console.error('[Auth] Error during custom token exchange:', error); - + console.error('[Auth] Error during custom token exchange or sign-in:', error); + // The UI will not change, and the user can try again. + // Optionally, send a generic error event to the renderer. const { windowPool } = require('./electron/windowManager'); const header = windowPool.get('header'); if (header) { - header.webContents.send('login-successful', { - error: 'authentication_failed', - message: error.message - }); + header.webContents.send('auth-failed', { message: error.message }); } } } @@ -462,7 +575,7 @@ async function startWebStack() { }); const createBackendApp = require('../pickleglass_web/backend_node'); - const nodeApi = createBackendApp(); + const nodeApi = createBackendApp(eventBridge); const staticDir = app.isPackaged ? path.join(process.resourcesPath, 'out') diff --git a/src/preload.js b/src/preload.js index 1b503da..5e9d369 100644 --- a/src/preload.js +++ b/src/preload.js @@ -1,41 +1,2 @@ // See the Electron documentation for details on how to use preload scripts: // https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts - -const { contextBridge, ipcRenderer } = require('electron'); - -contextBridge.exposeInMainWorld('ipcRenderer', { - send: (channel, data) => { - const validChannels = ['set-current-user', 'firebase-auth-success']; - if (validChannels.includes(channel)) { - ipcRenderer.send(channel, data); - } - }, - sendSync: (channel, data) => { - const validChannels = ['get-api-url-sync']; - if (validChannels.includes(channel)) { - return ipcRenderer.sendSync(channel, data); - } - }, - on: (channel, func) => { - const validChannels = ['api-key-updated']; - if (validChannels.includes(channel)) { - const newCallback = (_, ...args) => func(...args); - ipcRenderer.on(channel, newCallback); - return () => { - ipcRenderer.removeListener(channel, newCallback); - }; - } - }, - invoke: (channel, ...args) => { - const validChannels = ['save-api-key']; - if (validChannels.includes(channel)) { - return ipcRenderer.invoke(channel, ...args); - } - }, - removeAllListeners: (channel) => { - const validChannels = ['api-key-updated']; - if (validChannels.includes(channel)) { - ipcRenderer.removeAllListeners(channel); - } - }, -}); diff --git a/test-code-formatting.md b/test-code-formatting.md deleted file mode 100644 index a30d020..0000000 --- a/test-code-formatting.md +++ /dev/null @@ -1,50 +0,0 @@ -# Code Formatting Test - -Test various code blocks to ensure proper formatting: - -## Python Example (from screenshot) -```python -class Solution: - def subsets(self, nums: List[int]) -> List[List[int]]: - res = [] - - def backtrack(start, path): - res.append(path[:]) - for i in range(start, len(nums)): - path.append(nums[i]) - backtrack(i + 1, path) - path.pop() - - backtrack(0, []) - return res -``` - -## JavaScript Example -```javascript -function calculateSum(numbers) { - let sum = 0; - for (let i = 0; i < numbers.length; i++) { - sum += numbers[i]; - } - return sum; -} -``` - -## Indented Code with Complex Structure -```python -def complex_function(): - if True: - for i in range(10): - if i % 2 == 0: - print(f"Even: {i}") - else: - print(f"Odd: {i}") - if i > 5: - print(" Greater than 5") - nested = { - "key": "value", - "nested": { - "deep": "structure" - } - } -``` \ No newline at end of file