diff --git a/extensions/game-scout/.eslintrc.json b/extensions/game-scout/.eslintrc.json
new file mode 100644
index 00000000000..5bfc8b77671
--- /dev/null
+++ b/extensions/game-scout/.eslintrc.json
@@ -0,0 +1,9 @@
+{
+ "root": true,
+ "extends": [
+ "@raycast"
+ ],
+ "rules": {
+ "@typescript-eslint/no-explicit-any": "off"
+ }
+}
\ No newline at end of file
diff --git a/extensions/game-scout/.gitignore b/extensions/game-scout/.gitignore
new file mode 100644
index 00000000000..6a1e1786f3a
--- /dev/null
+++ b/extensions/game-scout/.gitignore
@@ -0,0 +1,14 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+
+# Raycast specific files
+
+raycast-env.d.ts
+.raycast-swift-build
+.swiftpm
+compiled_raycast_swift
+
+# misc
+.DS_Store
\ No newline at end of file
diff --git a/extensions/game-scout/CHANGELOG.md b/extensions/game-scout/CHANGELOG.md
new file mode 100644
index 00000000000..1d2662a8ea3
--- /dev/null
+++ b/extensions/game-scout/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Game Scout Changelog
+
+## [Initial Release] - {PR_MERGE_DATE}
+
+- Initial release
\ No newline at end of file
diff --git a/extensions/game-scout/README.md b/extensions/game-scout/README.md
new file mode 100644
index 00000000000..a02956b183a
--- /dev/null
+++ b/extensions/game-scout/README.md
@@ -0,0 +1,80 @@
+# Game Scout
+
+The ultimate gaming companion for Raycast. Search across multiple storefronts, track historical lows, discover free giveaways, and catch the best daily deals without leaving your launcher.
+
+## Features
+
+* **Smart Recommendation Engine:** Heuristic scoring evaluates current prices against All-Time Lows (ATL), medians, and active bundles to generate actionable verdicts (๐ฅ GREAT DEAL, ๐ GOOD DEAL, ๐ก FAIR PRICE, ๐ด HIGH PRICE).
+* **Price History Charts:** Visual price trend graphs (3-month, 6-month, 1-year ranges) generated directly in the detail view. *(Note: Can be toggled off in preferences to save API limits).*
+* **Bundle Content Viewer:** Drill down into active bundles to inspect pricing tiers, expiration dates, and included games.
+* **Search Games:** Quickly look up any game, see its current price, all-time low, and active bundles (via **IsThereAnyDeal API**).
+* **Saved Games:** Add games to your personal watchlist. Features advanced filtering (Only Deals, Biggest Discount, Best Opportunities) and a dynamic ๐ฅ Price Drops section.
+* **Top Deals:** Discover the highest-rated game deals across 30+ official stores, powered by the CheapShark Deal Rating algorithm (via **CheapShark API** - No API key required).
+* **Free Games:** Never miss a 100% free game or DLC giveaway across PC, PlayStation, Xbox, VR, and Mobile platforms (via **GamerPower API** - No API key required).
+* **Manage Stores:** Globally filter results across the extension so you only see prices from the storefronts you actually use.
+
+## Setup
+
+The **Top Deals**, **Free Games**, and **Manage Stores** commands work out of the box.
+
+To use the **Search** and **Saved Games** features, a free API key from IsThereAnyDeal is required:
+
+1. Create an account at [IsThereAnyDeal](https://isthereanydeal.com/).
+2. Go to the [Apps page](https://isthereanydeal.com/apps/) and click **Register App**.
+3. Enter a name for the application and click **Submit**.
+4. On your app's dashboard, locate the **API Keys** section on the right side.
+5. Copy the generated API key. *(Important: Use the API Key, **not** the OAuth Client ID or Client Secret on the left).*
+6. In Raycast, open the extension preferences and fill in:
+ * **IsThereAnyDeal API Key** โ the API key you generated.
+ * **Country** โ select your preferred region for pricing data.
+ * Configure optional preferences (e.g., max results, showing mature/DLC content, min discount, update frequency, and toggling the Price History Chart).
+
+## Commands
+
+| Command | Description |
+| :--- | :--- |
+| **Search Games** | Look up current prices, historical lows, and bundles for any game. |
+| **Saved Games** | Manage your personal watchlist and track active price drops. |
+| **Top Deals** | Browse the best daily discounts across 30+ official stores. |
+| **Free Games** | Find 100% free games, DLCs, and giveaways across all platforms. |
+| **Manage Stores** | Select which stores to include in your searches and deals. |
+
+## Actions
+
+### Global Actions
+* **Enter** โ View detailed information (prices, charts, bundles, instructions) in full-screen.
+
+### Search & Saved Games
+* **Cmd+S / Ctrl+S** โ Save / Remove game from your watchlist.
+* **Cmd+B / Ctrl+B** โ View bundle contents (if active bundles exist).
+* **Cmd+R / Ctrl+R** โ Force refresh price and chart data for the current game.
+
+### Game Detail View (Search & Saved Games)
+* **Cmd+C / Ctrl+C** โ Copy best deal link.
+* **Cmd+Shift+C / Ctrl+Shift+C** โ Copy game name.
+
+### Saved Games Specific
+* **Cmd+Shift+Backspace / Ctrl+Shift+Backspace** โ Clear all saved games.
+
+### Top Deals
+* **Cmd+M / Ctrl+M** โ View Metacritic reviews (if available).
+* **Cmd+Shift+C / Ctrl+Shift+C** โ Copy deal link.
+* **Cmd+Alt+Shift+C / Ctrl+Alt+Shift+C** โ Copy Metacritic link (if available).
+
+### Free Games
+* **Cmd+I / Ctrl+I** โ Ignore / Restore giveaway (hides from the main list).
+
+### Manage Stores
+* **Cmd+Shift+A / Ctrl+Shift+A** โ Select all stores.
+* **Cmd+Shift+D / Ctrl+Shift+D** โ Deselect all stores.
+
+## Troubleshooting
+
+* **Getting "No Results" or "Invalid API Key" toast?** Double-check that you copied the **API Key** (from the right column) and not the OAuth Client ID from your IsThereAnyDeal app dashboard.
+* **Hitting API Rate Limits?** If you check hundreds of games daily, try turning off the `Show Price History Chart` setting in the extension preferences to save 1 API call per game lookup.
+
+## Support
+
+If this extension helps you find great deals, consider buying me a coffee!
+
+
\ No newline at end of file
diff --git a/extensions/game-scout/assets/extension-icon.png b/extensions/game-scout/assets/extension-icon.png
new file mode 100644
index 00000000000..5ba238a1c6d
Binary files /dev/null and b/extensions/game-scout/assets/extension-icon.png differ
diff --git a/extensions/game-scout/metadata/1.png b/extensions/game-scout/metadata/1.png
new file mode 100644
index 00000000000..9b6933669b8
Binary files /dev/null and b/extensions/game-scout/metadata/1.png differ
diff --git a/extensions/game-scout/metadata/2.png b/extensions/game-scout/metadata/2.png
new file mode 100644
index 00000000000..5f0e8a28a67
Binary files /dev/null and b/extensions/game-scout/metadata/2.png differ
diff --git a/extensions/game-scout/metadata/3.png b/extensions/game-scout/metadata/3.png
new file mode 100644
index 00000000000..13203b3af9c
Binary files /dev/null and b/extensions/game-scout/metadata/3.png differ
diff --git a/extensions/game-scout/metadata/4.png b/extensions/game-scout/metadata/4.png
new file mode 100644
index 00000000000..e602b1b5792
Binary files /dev/null and b/extensions/game-scout/metadata/4.png differ
diff --git a/extensions/game-scout/metadata/5.png b/extensions/game-scout/metadata/5.png
new file mode 100644
index 00000000000..e7592102df7
Binary files /dev/null and b/extensions/game-scout/metadata/5.png differ
diff --git a/extensions/game-scout/metadata/6.png b/extensions/game-scout/metadata/6.png
new file mode 100644
index 00000000000..b046cc78171
Binary files /dev/null and b/extensions/game-scout/metadata/6.png differ
diff --git a/extensions/game-scout/metadata/7.png b/extensions/game-scout/metadata/7.png
new file mode 100644
index 00000000000..69b1d36205e
Binary files /dev/null and b/extensions/game-scout/metadata/7.png differ
diff --git a/extensions/game-scout/package-lock.json b/extensions/game-scout/package-lock.json
new file mode 100644
index 00000000000..7d5eb4619e6
--- /dev/null
+++ b/extensions/game-scout/package-lock.json
@@ -0,0 +1,3473 @@
+{
+ "name": "game-scout",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "game-scout",
+ "license": "MIT",
+ "dependencies": {
+ "@raycast/api": "^1.104.12",
+ "@raycast/utils": "^2.2.3"
+ },
+ "devDependencies": {
+ "@raycast/eslint-config": "^1.0.8",
+ "@types/node": "^25.6.0",
+ "@types/react": "^19.2.14",
+ "eslint": "^8.57.0",
+ "prettier": "^3.8.3",
+ "typescript": "^6.0.3"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "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/eslintrc/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/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "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/@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/config-array/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/@humanwhocodes/config-array/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "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/@inquirer/ansi": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz",
+ "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@inquirer/checkbox": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz",
+ "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/confirm": {
+ "version": "5.1.21",
+ "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz",
+ "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/core": {
+ "version": "10.3.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz",
+ "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
+ "cli-width": "^4.1.0",
+ "mute-stream": "^2.0.0",
+ "signal-exit": "^4.1.0",
+ "wrap-ansi": "^6.2.0",
+ "yoctocolors-cjs": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/core/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@inquirer/editor": {
+ "version": "4.2.23",
+ "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz",
+ "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/external-editor": "^1.0.3",
+ "@inquirer/type": "^3.0.10"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/expand": {
+ "version": "4.0.23",
+ "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz",
+ "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/external-editor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
+ "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^2.1.1",
+ "iconv-lite": "^0.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/figures": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz",
+ "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@inquirer/input": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz",
+ "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/number": {
+ "version": "3.0.23",
+ "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz",
+ "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/password": {
+ "version": "4.0.23",
+ "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz",
+ "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/prompts": {
+ "version": "7.10.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz",
+ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/checkbox": "^4.3.2",
+ "@inquirer/confirm": "^5.1.21",
+ "@inquirer/editor": "^4.2.23",
+ "@inquirer/expand": "^4.0.23",
+ "@inquirer/input": "^4.3.1",
+ "@inquirer/number": "^3.0.23",
+ "@inquirer/password": "^4.0.23",
+ "@inquirer/rawlist": "^4.1.11",
+ "@inquirer/search": "^3.2.2",
+ "@inquirer/select": "^4.4.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/rawlist": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz",
+ "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/search": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz",
+ "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/select": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz",
+ "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/ansi": "^1.0.2",
+ "@inquirer/core": "^10.3.2",
+ "@inquirer/figures": "^1.0.15",
+ "@inquirer/type": "^3.0.10",
+ "yoctocolors-cjs": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/type": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz",
+ "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "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/@oclif/core": {
+ "version": "4.10.5",
+ "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.5.tgz",
+ "integrity": "sha512-qcdCF7NrdWPfme6Kr34wwljRCXbCVpL1WVxiNy0Ep6vbWKjxAjFQwuhqkoyL0yjI+KdwtLcOCGn5z2yzdijc8w==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^4.3.2",
+ "ansis": "^3.17.0",
+ "clean-stack": "^3.0.1",
+ "cli-spinners": "^2.9.2",
+ "debug": "^4.4.3",
+ "ejs": "^3.1.10",
+ "get-package-type": "^0.1.0",
+ "indent-string": "^4.0.0",
+ "is-wsl": "^2.2.0",
+ "lilconfig": "^3.1.3",
+ "minimatch": "^10.2.5",
+ "semver": "^7.7.3",
+ "string-width": "^4.2.3",
+ "supports-color": "^8",
+ "tinyglobby": "^0.2.14",
+ "widest-line": "^3.1.0",
+ "wordwrap": "^1.0.0",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@oclif/plugin-autocomplete": {
+ "version": "3.2.45",
+ "resolved": "https://registry.npmjs.org/@oclif/plugin-autocomplete/-/plugin-autocomplete-3.2.45.tgz",
+ "integrity": "sha512-ENrUg8rbVCjh40uvi3MC9kGbiUoEf11nyqE59RBzegeeLpRXNo/Zp27L9j1tUmPEqGgfS2/wvHPihNzkpK1FDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@oclif/core": "^4",
+ "ansis": "^3.16.0",
+ "debug": "^4.4.1",
+ "ejs": "^3.1.10"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@oclif/plugin-help": {
+ "version": "6.2.44",
+ "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.44.tgz",
+ "integrity": "sha512-x03Se2LtlOOlGfTuuubt5C4Z8NHeR4zKXtVnfycuLU+2VOMu2WpsGy9nbs3nYuInuvsIY1BizjVaTjUz060Sig==",
+ "license": "MIT",
+ "dependencies": {
+ "@oclif/core": "^4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@oclif/plugin-not-found": {
+ "version": "3.2.80",
+ "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.80.tgz",
+ "integrity": "sha512-yTLjWvR1r/Rd/cO2LxHdMCDoL5sQhBYRUcOMCmxZtWVWhx4rAZ8KVUPDVsb+SvjJDV5ADTDBgt1H52fFx7YWqg==",
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/prompts": "^7.10.1",
+ "@oclif/core": "^4.10.5",
+ "ansis": "^3.17.0",
+ "fast-levenshtein": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@raycast/api": {
+ "version": "1.104.12",
+ "resolved": "https://registry.npmjs.org/@raycast/api/-/api-1.104.12.tgz",
+ "integrity": "sha512-DdrtoksnzLw4q60BgFr/H+PIvIObOfJrW15duTFH7QXVx/0Vxzw9fY7wo+H2gQ2JDDAh9sEMCpc5akP3UxKjTw==",
+ "license": "MIT",
+ "dependencies": {
+ "@oclif/core": "^4.8.4",
+ "@oclif/plugin-autocomplete": "^3.2.40",
+ "@oclif/plugin-help": "^6.2.37",
+ "@oclif/plugin-not-found": "^3.2.74",
+ "@types/node": "22.13.10",
+ "@types/react": "19.0.10",
+ "esbuild": "^0.27.3",
+ "react": "19.0.0"
+ },
+ "bin": {
+ "ray": "bin/run.js"
+ },
+ "engines": {
+ "node": ">=22.14.0"
+ },
+ "peerDependencies": {
+ "@types/node": "22.13.10",
+ "@types/react": "19.0.10",
+ "react-devtools": "6.1.1"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ },
+ "react-devtools": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@raycast/api/node_modules/@types/node": {
+ "version": "22.13.10",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
+ "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.20.0"
+ }
+ },
+ "node_modules/@raycast/api/node_modules/@types/react": {
+ "version": "19.0.10",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
+ "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@raycast/api/node_modules/undici-types": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+ "license": "MIT"
+ },
+ "node_modules/@raycast/eslint-config": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@raycast/eslint-config/-/eslint-config-1.0.11.tgz",
+ "integrity": "sha512-I0Lt8bwahVGkANUBxripIxKptMBz1Ou+UXGwfqgFvKwo1gVLrnlEngxaspQJA8L5pvzQkQMwizVCSgNC3bddWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@raycast/eslint-plugin": "^1.0.11",
+ "@rushstack/eslint-patch": "^1.10.4",
+ "@typescript-eslint/eslint-plugin": "^6.8.0",
+ "@typescript-eslint/parser": "^6.8.0",
+ "eslint-config-prettier": "^9.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7",
+ "prettier": ">=2",
+ "typescript": ">=4"
+ }
+ },
+ "node_modules/@raycast/eslint-config/node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
+ "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.5.1",
+ "@typescript-eslint/scope-manager": "6.21.0",
+ "@typescript-eslint/type-utils": "6.21.0",
+ "@typescript-eslint/utils": "6.21.0",
+ "@typescript-eslint/visitor-keys": "6.21.0",
+ "debug": "^4.3.4",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.4",
+ "natural-compare": "^1.4.0",
+ "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"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
+ "eslint": "^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@raycast/eslint-config/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
+ "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "6.21.0",
+ "@typescript-eslint/utils": "6.21.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.0.1"
+ },
+ "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/@raycast/eslint-config/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
+ "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@types/json-schema": "^7.0.12",
+ "@types/semver": "^7.5.0",
+ "@typescript-eslint/scope-manager": "6.21.0",
+ "@typescript-eslint/types": "6.21.0",
+ "@typescript-eslint/typescript-estree": "6.21.0",
+ "semver": "^7.5.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"
+ }
+ },
+ "node_modules/@raycast/eslint-config/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/@raycast/eslint-plugin": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/@raycast/eslint-plugin/-/eslint-plugin-1.0.16.tgz",
+ "integrity": "sha512-OyFL/W75/4hlgdUUI80Eoes0HjpVrJ8I1kB/PBH2RLjbcK22TC6IwZPXvhBZ5jF962O1TqtOuHrTjySwDaa/cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "^5.62.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7"
+ }
+ },
+ "node_modules/@raycast/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
+ "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/visitor-keys": "5.62.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@raycast/eslint-plugin/node_modules/@typescript-eslint/types": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
+ "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@raycast/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
+ "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/visitor-keys": "5.62.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@raycast/eslint-plugin/node_modules/@typescript-eslint/utils": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
+ "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@types/json-schema": "^7.0.9",
+ "@types/semver": "^7.3.12",
+ "@typescript-eslint/scope-manager": "5.62.0",
+ "@typescript-eslint/types": "5.62.0",
+ "@typescript-eslint/typescript-estree": "5.62.0",
+ "eslint-scope": "^5.1.1",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@raycast/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.62.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
+ "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.62.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@raycast/eslint-plugin/node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@raycast/eslint-plugin/node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/@raycast/utils": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/@raycast/utils/-/utils-2.2.3.tgz",
+ "integrity": "sha512-YwqleVl0Wk/FOq+672gtvswuwMqjNqIr+c63FhouTEHc1LJ3zaohLSkW4+UcfBjPU8HySKQm+kJqZW1iOM9fnA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ },
+ "peerDependencies": {
+ "@raycast/api": ">=1.99.4",
+ "react": ">=19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rushstack/eslint-patch": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz",
+ "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "25.6.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
+ "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.19.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.14",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
+ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/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/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "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/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "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.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+ "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-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/ansis": {
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz",
+ "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "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/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/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "license": "MIT"
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "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/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/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/chalk/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/chardet": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz",
+ "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
+ "license": "MIT"
+ },
+ "node_modules/clean-stack": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz",
+ "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+ "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+ "license": "ISC",
+ "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/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "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/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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/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/ejs": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jake": "^10.8.5"
+ },
+ "bin": {
+ "ejs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.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/esbuild": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
+ }
+ },
+ "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==",
+ "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-prettier": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz",
+ "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "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/eslint/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/eslint/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "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.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "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==",
+ "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-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==",
+ "dev": true,
+ "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": "3.0.0",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz",
+ "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fastest-levenshtein": "^1.0.7"
+ }
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "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/filelist": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
+ "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.0.1"
+ }
+ },
+ "node_modules/filelist/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/filelist/node_modules/brace-expansion": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/filelist/node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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/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/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.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/get-package-type": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "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": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/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/glob/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "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/globals/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/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/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-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "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/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==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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-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-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-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/jake": {
+ "version": "10.9.4",
+ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
+ "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "async": "^3.2.6",
+ "filelist": "^1.0.4",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "jake": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "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/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/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/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.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/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/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/micromatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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/mute-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
+ "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "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/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/optionator/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/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/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "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/prettier": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
+ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=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/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/react": {
+ "version": "19.0.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
+ "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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/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/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==",
+ "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/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/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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/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/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/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-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/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "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/tinyglobby": {
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "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/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/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
+ "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "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.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
+ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.19.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
+ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "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/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/widest-line": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+ "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.0.0"
+ },
+ "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",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "license": "MIT"
+ },
+ "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/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/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"
+ }
+ },
+ "node_modules/yoctocolors-cjs": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+ "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/extensions/game-scout/package.json b/extensions/game-scout/package.json
new file mode 100644
index 00000000000..113c3759f33
--- /dev/null
+++ b/extensions/game-scout/package.json
@@ -0,0 +1,355 @@
+{
+ "name": "game-scout",
+ "title": "Game Scout",
+ "description": "The ultimate gaming companion to track prices, deals, and free giveaways.",
+ "icon": "extension-icon.png",
+ "author": "glct26",
+ "categories": [
+ "Fun",
+ "Web"
+ ],
+ "license": "MIT",
+ "commands": [
+ {
+ "name": "search-games",
+ "title": "Search Games",
+ "description": "Search for game prices and deals",
+ "mode": "view"
+ },
+ {
+ "name": "saved-games",
+ "title": "Saved Games",
+ "description": "Track your saved games and their prices.",
+ "mode": "view"
+ },
+ {
+ "name": "top-deals",
+ "title": "Top Deals",
+ "description": "View current best deals",
+ "mode": "view"
+ },
+ {
+ "name": "free-games",
+ "title": "Free Games",
+ "description": "Discover currently free games and giveaways.",
+ "mode": "view"
+ },
+{
+ "name": "manage-stores",
+ "title": "Manage Stores",
+ "description": "Select which stores to include in your searches and deals.",
+ "mode": "view"
+ }
+ ],
+ "preferences": [
+ {
+ "name": "itadApiKey",
+ "type": "textfield",
+ "required": false,
+ "title": "ITAD API Key",
+ "description": "Your IsThereAnyDeal API Key"
+ },
+ {
+ "name": "country",
+ "type": "dropdown",
+ "required": false,
+ "title": "Country",
+ "description": "Select your store region",
+ "default": "US",
+ "data": [
+ {
+ "title": "United States",
+ "value": "US"
+ },
+ {
+ "title": "United Kingdom",
+ "value": "GB"
+ },
+ {
+ "title": "France",
+ "value": "FR"
+ },
+ {
+ "title": "Australia",
+ "value": "AU"
+ },
+ {
+ "title": "Canada",
+ "value": "CA"
+ },
+ {
+ "title": "Brazil",
+ "value": "BR"
+ },
+ {
+ "title": "China",
+ "value": "CN"
+ },
+ {
+ "title": "Turkey",
+ "value": "TR"
+ },
+ {
+ "title": "India",
+ "value": "IN"
+ },
+ {
+ "title": "South Korea",
+ "value": "KR"
+ },
+ {
+ "title": "Japan",
+ "value": "JP"
+ },
+ {
+ "title": "Indonesia",
+ "value": "ID"
+ },
+ {
+ "title": "Taiwan",
+ "value": "TW"
+ },
+ {
+ "title": "โโโโโโโโโโ",
+ "value": ""
+ },
+ {
+ "title": "Argentina",
+ "value": "AR"
+ },
+ {
+ "title": "Chile",
+ "value": "CL"
+ },
+ {
+ "title": "Colombia",
+ "value": "CO"
+ },
+ {
+ "title": "Czech Republic",
+ "value": "CZ"
+ },
+ {
+ "title": "Denmark",
+ "value": "DK"
+ },
+ {
+ "title": "Germany",
+ "value": "DE"
+ },
+ {
+ "title": "Hong Kong",
+ "value": "HK"
+ },
+ {
+ "title": "Hungary",
+ "value": "HU"
+ },
+ {
+ "title": "Israel",
+ "value": "IL"
+ },
+ {
+ "title": "Kazakhstan",
+ "value": "KZ"
+ },
+ {
+ "title": "Malaysia",
+ "value": "MY"
+ },
+ {
+ "title": "Mexico",
+ "value": "MX"
+ },
+ {
+ "title": "New Zealand",
+ "value": "NZ"
+ },
+ {
+ "title": "Norway",
+ "value": "NO"
+ },
+ {
+ "title": "Peru",
+ "value": "PE"
+ },
+ {
+ "title": "Philippines",
+ "value": "PH"
+ },
+ {
+ "title": "Poland",
+ "value": "PL"
+ },
+ {
+ "title": "Qatar",
+ "value": "QA"
+ },
+ {
+ "title": "Russia",
+ "value": "RU"
+ },
+ {
+ "title": "Saudi Arabia",
+ "value": "SA"
+ },
+ {
+ "title": "Singapore",
+ "value": "SG"
+ },
+ {
+ "title": "South Africa",
+ "value": "ZA"
+ },
+ {
+ "title": "Sweden",
+ "value": "SE"
+ },
+ {
+ "title": "Switzerland",
+ "value": "CH"
+ },
+ {
+ "title": "Thailand",
+ "value": "TH"
+ },
+ {
+ "title": "Ukraine",
+ "value": "UA"
+ },
+ {
+ "title": "United Arab Emirates",
+ "value": "AE"
+ },
+ {
+ "title": "Uruguay",
+ "value": "UY"
+ }
+ ]
+ },
+ {
+ "name": "showMature",
+ "type": "checkbox",
+ "required": false,
+ "title": "Content Filters",
+ "label": "[Game Search] Show mature (18+) games",
+ "description": "Include games with mature content in search results.",
+ "default": false
+ },
+ {
+ "name": "showDLCGameSearch",
+ "type": "checkbox",
+ "required": false,
+ "title": "Content Filters",
+ "label": "[Game Search] Show DLCs",
+ "description": "Include downloadable content in game search results.",
+ "default": true
+ },
+ {
+ "name": "maxResults",
+ "type": "dropdown",
+ "required": false,
+ "title": "[Game Search] Max Search Results",
+ "description": "Number of games to fetch per search.",
+ "default": "25",
+ "data": [
+ {
+ "title": "10 Results",
+ "value": "10"
+ },
+ {
+ "title": "25 Results",
+ "value": "25"
+ },
+ {
+ "title": "50 Results",
+ "value": "50"
+ }
+ ]
+ },
+ {
+ "name": "refreshFrequency",
+ "type": "dropdown",
+ "required": false,
+ "title": "[Saved Games] Update Frequency",
+ "description": "How often to check for price updates (helps to save API limits).",
+ "default": "12",
+ "data": [
+ {
+ "title": "Every 6 Hours",
+ "value": "6"
+ },
+ {
+ "title": "Every 12 Hours",
+ "value": "12"
+ },
+ {
+ "title": "Every 24 Hours",
+ "value": "24"
+ }
+ ]
+ },
+ {
+ "name": "minDiscount",
+ "type": "textfield",
+ "required": false,
+ "title": "[Top Deals] Min Discount %",
+ "description": "Show deals with at least this savings percentage (e.g., 50).",
+ "placeholder": "0"
+ },
+ {
+ "name": "maxPrice",
+ "type": "textfield",
+ "required": false,
+ "title": "[Top Deals] Max Price ($)",
+ "description": "Show deals below this price in USD.",
+ "placeholder": "e.g., 50"
+ },
+ {
+ "name": "showDLCFreeGames",
+ "type": "checkbox",
+ "required": false,
+ "title": "",
+ "label": "[Free Games] Show DLCs",
+ "description": "Include downloadable content in the free games list.",
+ "default": true
+ },
+ {
+ "name": "showPriceHistoryChart",
+ "type": "checkbox",
+ "required": false,
+ "title": "API & Performance",
+ "label": "[Game Details] Show Price History Chart",
+ "description": "Warning: Uses 1 extra API call per game to generate a 1-year price chart. Turn off if you check hundreds of games daily.",
+ "default": true
+ }
+ ],
+ "dependencies": {
+ "@raycast/api": "^1.104.12",
+ "@raycast/utils": "^2.2.3"
+ },
+ "devDependencies": {
+ "@raycast/eslint-config": "^1.0.8",
+ "@types/node": "^25.6.0",
+ "@types/react": "^19.2.14",
+ "eslint": "^8.57.0",
+ "prettier": "^3.8.3",
+ "typescript": "^6.0.3"
+ },
+ "screenshots": [
+ { "path": "metadata/1.png", "title": "Search Games" },
+ { "path": "metadata/2.png", "title": "Saved Games" },
+ { "path": "metadata/3.png", "title": "Game Detail - SKIP & Bundle" },
+ { "path": "metadata/4.png", "title": "Game Detail - BUY & Chart" },
+ { "path": "metadata/5.png", "title": "Free Games" },
+ { "path": "metadata/6.png", "title": "Top Deals" },
+ { "path": "metadata/7.png", "title": "Manage Stores" }
+ ],
+ "scripts": {
+ "dev": "ray dev",
+ "build": "ray build -e dist",
+ "fix-lint": "ray lint --fix",
+ "lint": "ray lint",
+ "publish": "npx @raycast/api@latest publish"
+ }
+}
diff --git a/extensions/game-scout/src/free-games.tsx b/extensions/game-scout/src/free-games.tsx
new file mode 100644
index 00000000000..33283d5ccb2
--- /dev/null
+++ b/extensions/game-scout/src/free-games.tsx
@@ -0,0 +1,401 @@
+import { useEffect, useState } from "react";
+import {
+ List,
+ ActionPanel,
+ Action,
+ Icon,
+ Color,
+ Detail,
+ LocalStorage,
+ getPreferenceValues,
+} from "@raycast/api";
+import { useFetch } from "@raycast/utils";
+
+interface Giveaway {
+ id: number;
+ title: string;
+ worth: string;
+ thumbnail: string;
+ image: string;
+ description: string;
+ instructions: string;
+ open_giveaway: string;
+ published_date: string;
+ type: string;
+ platforms: string;
+ end_date: string;
+ users: number;
+ status: string;
+}
+
+function parsePlatformsAndStores(platformsString: string) {
+ const parts = platformsString.split(", ").map((p) => p.trim());
+
+ const categories = {
+ stores: [] as string[],
+ pc: [] as string[],
+ playstation: [] as string[],
+ xbox: [] as string[],
+ switch: [] as string[],
+ mobile: [] as string[],
+ vr: [] as string[],
+ };
+
+ parts.forEach((p) => {
+ const lowerP = p.toLowerCase();
+
+ if (
+ lowerP.includes("steam") ||
+ lowerP.includes("epic") ||
+ lowerP.includes("gog")
+ ) {
+ categories.stores.push(p);
+ } else if (lowerP === "pc" || lowerP === "windows") {
+ categories.pc.push(p);
+ } else if (
+ lowerP.includes("playstation") ||
+ lowerP.includes("ps3") ||
+ lowerP.includes("ps4") ||
+ lowerP.includes("ps5") ||
+ lowerP.includes("vita")
+ ) {
+ categories.playstation.push(p);
+ } else if (lowerP.includes("xbox")) {
+ categories.xbox.push(p);
+ } else if (lowerP.includes("switch") || lowerP.includes("nintendo")) {
+ categories.switch.push(p);
+ } else if (
+ lowerP.includes("android") ||
+ lowerP.includes("ios") ||
+ lowerP.includes("mobile")
+ ) {
+ categories.mobile.push(p);
+ } else if (lowerP.includes("vr")) {
+ categories.vr.push(p);
+ } else {
+ categories.stores.push(p);
+ }
+ });
+
+ const accessories: any[] = [];
+
+ categories.stores.forEach((store) => {
+ let tagColor = Color.SecondaryText;
+ const lower = store.toLowerCase();
+ if (lower.includes("steam")) tagColor = Color.Blue;
+ else if (lower.includes("epic")) tagColor = Color.PrimaryText;
+ else if (lower.includes("gog")) tagColor = Color.Purple;
+ accessories.push({
+ tag: { value: store, color: tagColor },
+ tooltip: "Storefront",
+ });
+ });
+
+ if (categories.pc.length > 0) {
+ accessories.push({
+ icon: { source: Icon.Monitor, tintColor: Color.SecondaryText },
+ tooltip: categories.pc.join(", "),
+ });
+ }
+ if (categories.playstation.length > 0) {
+ accessories.push({
+ icon: { source: Icon.GameController, tintColor: Color.Blue },
+ tooltip: categories.playstation.join(", "),
+ });
+ }
+ if (categories.xbox.length > 0) {
+ accessories.push({
+ icon: { source: Icon.GameController, tintColor: Color.Green },
+ tooltip: categories.xbox.join(", "),
+ });
+ }
+ if (categories.switch.length > 0) {
+ accessories.push({
+ icon: { source: Icon.GameController, tintColor: Color.Red },
+ tooltip: categories.switch.join(", "),
+ });
+ }
+ if (categories.mobile.length > 0) {
+ accessories.push({
+ icon: { source: Icon.Mobile, tintColor: Color.Yellow },
+ tooltip: categories.mobile.join(", "),
+ });
+ }
+ if (categories.vr.length > 0) {
+ accessories.push({
+ icon: { source: Icon.Eye, tintColor: Color.Orange },
+ tooltip: categories.vr.join(", "),
+ });
+ }
+
+ return accessories;
+}
+
+export default function FreeGames() {
+ const preferences = getPreferenceValues();
+ const { isLoading, data } = useFetch(
+ "https://www.gamerpower.com/api/giveaways",
+ );
+
+ const [ignoredIds, setIgnoredIds] = useState([]);
+ const [viewFilter, setViewFilter] = useState("all");
+
+ useEffect(() => {
+ LocalStorage.getItem("ignored_giveaways").then((storedIds) => {
+ if (storedIds) setIgnoredIds(JSON.parse(storedIds));
+ });
+ }, []);
+
+ const toggleIgnore = async (id: number) => {
+ let newIds;
+ if (ignoredIds.includes(id)) {
+ newIds = ignoredIds.filter((i) => i !== id);
+ } else {
+ newIds = [...ignoredIds, id];
+ }
+ setIgnoredIds(newIds);
+ await LocalStorage.setItem("ignored_giveaways", JSON.stringify(newIds));
+ };
+
+ const filteredData = data?.filter((game) => {
+ const isIgnored = ignoredIds.includes(game.id);
+ const platforms = game.platforms.toLowerCase();
+ const type = game.type.toLowerCase();
+
+ if (!preferences.showDLCFreeGames && type.includes("dlc")) {
+ return false;
+ }
+
+ if (viewFilter === "ignored") return isIgnored;
+ if (isIgnored) return false;
+
+ if (viewFilter === "all") return true;
+ if (viewFilter === "pc")
+ return platforms.includes("pc") || platforms.includes("windows");
+ if (viewFilter === "ps")
+ return (
+ platforms.includes("playstation") ||
+ platforms.includes("ps3") ||
+ platforms.includes("ps4") ||
+ platforms.includes("ps5")
+ );
+ if (viewFilter === "xbox") return platforms.includes("xbox");
+ if (viewFilter === "switch")
+ return platforms.includes("switch") || platforms.includes("nintendo");
+ if (viewFilter === "mobile")
+ return platforms.includes("android") || platforms.includes("ios");
+ if (viewFilter === "vr") return platforms.includes("vr");
+
+ return true;
+ });
+
+ return (
+ setViewFilter(newValue)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ >
+ {filteredData?.length === 0 && !isLoading && (
+
+ )}
+
+ {filteredData?.map((game) => {
+ const platformAccessories = parsePlatformsAndStores(game.platforms);
+ const isIgnored = ignoredIds.includes(game.id);
+ const searchKeywords = game.platforms.split(", ");
+
+ return (
+
+
+ toggleIgnore(game.id)}
+ isIgnored={isIgnored}
+ />
+ }
+ icon={Icon.Sidebar}
+ />
+
+
+
+ toggleIgnore(game.id)}
+ icon={isIgnored ? Icon.Eye : Icon.EyeDisabled}
+ shortcut={{
+ Windows: { modifiers: ["ctrl"], key: "i" },
+ macOS: { modifiers: ["cmd"], key: "i" },
+ }}
+ />
+
+
+ }
+ />
+ );
+ })}
+
+ );
+}
+
+function GiveawayDetail({
+ game,
+ toggleIgnore,
+ isIgnored,
+}: {
+ game: Giveaway;
+ toggleIgnore: () => void;
+ isIgnored: boolean;
+}) {
+ const endDate =
+ game.end_date !== "N/A"
+ ? new Date(game.end_date).toLocaleDateString("en-GB")
+ : "No end date";
+
+ const markdown = `
+
+
+# ${game.title}
+
+${game.description}
+
+---
+
+### How to claim:
+${game.instructions}
+ `;
+
+ return (
+
+
+
+
+ {game.platforms.split(", ").map((p, index) => {
+ const isStore =
+ p.toLowerCase().includes("steam") ||
+ p.toLowerCase().includes("epic") ||
+ p.toLowerCase().includes("gog");
+ return (
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+ }
+ actions={
+
+
+
+
+ }
+ />
+ );
+}
diff --git a/extensions/game-scout/src/manage-stores.tsx b/extensions/game-scout/src/manage-stores.tsx
new file mode 100644
index 00000000000..15c5e6ee950
--- /dev/null
+++ b/extensions/game-scout/src/manage-stores.tsx
@@ -0,0 +1,139 @@
+import { useState, useEffect } from "react";
+import {
+ List,
+ ActionPanel,
+ Action,
+ Icon,
+ LocalStorage,
+ showToast,
+ Toast,
+ Color,
+} from "@raycast/api";
+
+// Unified store list for both ITAD and CheapShark
+export const UNIFIED_STORES = [
+ { id: "2game", name: "2game" },
+ { id: "allyouplay", name: "AllYouPlay" },
+ { id: "blizzard", name: "Blizzard Shop" },
+ { id: "dlgamer", name: "DLGamer" },
+ { id: "ea", name: "EA Store (Origin)" },
+ { id: "epic", name: "Epic Games Store" },
+ { id: "etailmarket", name: "eTail.Market" },
+ { id: "fanatical", name: "Fanatical" },
+ { id: "gamebillet", name: "GameBillet" },
+ { id: "gamersgate", name: "GamersGate" },
+ { id: "gamesplanet", name: "GamesPlanet" },
+ { id: "gog", name: "GOG" },
+ { id: "gmg", name: "Green Man Gaming" },
+ { id: "humble", name: "Humble Store" },
+ { id: "indiegala", name: "IndieGala" },
+ { id: "joybuggy", name: "JoyBuggy" },
+ { id: "microsoft", name: "Microsoft Store" },
+ { id: "planetplay", name: "PlanetPlay" },
+ { id: "steam", name: "Steam" },
+ { id: "ubisoft", name: "Ubisoft Store" },
+ { id: "voidu", name: "Voidu" },
+ { id: "wingamestore", name: "WinGameStore / MacGameStore" },
+ { id: "other", name: "Other (Unlisted Stores)" },
+];
+
+export default function ManageStores() {
+ const [selectedIds, setSelectedIds] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+
+ useEffect(() => {
+ const init = async () => {
+ const stored = await LocalStorage.getItem("selected_stores");
+ if (stored) {
+ setSelectedIds(JSON.parse(stored));
+ } else {
+ setSelectedIds(UNIFIED_STORES.map((s) => s.id));
+ }
+ setIsLoading(false);
+ };
+ init();
+ }, []);
+
+ const toggleStore = async (id: string) => {
+ const isCurrentlySelected = selectedIds.includes(id);
+
+ if (isCurrentlySelected && selectedIds.length === 1) {
+ await showToast({
+ style: Toast.Style.Failure,
+ title: "Cannot Deselect",
+ message: "At least one store must remain selected.",
+ });
+ return;
+ }
+
+ const newIds = isCurrentlySelected
+ ? selectedIds.filter((sId) => sId !== id)
+ : [...selectedIds, id];
+
+ setSelectedIds(newIds);
+ await LocalStorage.setItem("selected_stores", JSON.stringify(newIds));
+ };
+
+ const selectAll = async () => {
+ const allIds = UNIFIED_STORES.map((s) => s.id);
+ setSelectedIds(allIds);
+ await LocalStorage.setItem("selected_stores", JSON.stringify(allIds));
+ };
+
+ const selectNone = () => {
+ setSelectedIds([]);
+ // Don't save to the LocalStorage
+ };
+
+ return (
+
+ {UNIFIED_STORES.map((store) => {
+ const isSelected = selectedIds.includes(store.id);
+
+ return (
+
+
+ toggleStore(store.id)}
+ icon={isSelected ? Icon.Circle : Icon.CheckCircle}
+ />
+
+
+
+
+
+
+ }
+ />
+ );
+ })}
+
+ );
+}
diff --git a/extensions/game-scout/src/saved-games.tsx b/extensions/game-scout/src/saved-games.tsx
new file mode 100644
index 00000000000..3f6e8659182
--- /dev/null
+++ b/extensions/game-scout/src/saved-games.tsx
@@ -0,0 +1,1400 @@
+// TODO: Add 24h timestamp to referencePrices for time-based price change tags
+
+import { useEffect, useState, useMemo } from "react";
+import {
+ List,
+ ActionPanel,
+ Action,
+ Icon,
+ Color,
+ Detail,
+ LocalStorage,
+ getPreferenceValues,
+ Cache,
+ openExtensionPreferences,
+ Image,
+} from "@raycast/api";
+
+const preferences = getPreferenceValues<{
+ itadApiKey: string;
+ country: string;
+ refreshFrequency: string;
+ showPriceHistoryChart: boolean;
+}>();
+
+const API_KEY = (preferences.itadApiKey || "").trim();
+const COUNTRY = preferences.country;
+
+const cache = new Cache();
+const CACHE_KEY = `itad_saved_prices_v1_${COUNTRY}`;
+const CACHE_TTL =
+ parseInt(preferences.refreshFrequency || "12") * 60 * 60 * 1000;
+const DETAIL_CACHE_TTL = 6 * 60 * 60 * 1000;
+const RECENT_BUNDLE_WINDOW = 2 * 365 * 24 * 60 * 60 * 1000;
+
+import { formatPrice, isStoreAllowed } from "./utils";
+
+const cleanBundleUrl = (url?: string) => {
+ if (!url) return "";
+ if (url.includes("u=http")) {
+ try {
+ return decodeURIComponent(url.split("u=")[1].split("&")[0]);
+ } catch {
+ return url;
+ }
+ }
+ return url;
+};
+
+export default function SavedGames() {
+ const isApiKeyValid = API_KEY.length > 0;
+ if (!isApiKeyValid)
+ return (
+
+
+
+
+ }
+ />
+
+ );
+
+ const [savedGames, setSavedGames] = useState<
+ { id: string; title: string; slug: string; type?: string }[]
+ >([]);
+ const [rawPrices, setRawPrices] = useState>({});
+ const [bundleCounts, setBundleCounts] = useState>({});
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [refreshKey, setRefreshKey] = useState(0);
+ const [referencePrices, setReferencePrices] = useState<
+ Record
+ >({});
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedStores, setSelectedStores] = useState(["all"]);
+ const [filterMode, setFilterMode] = useState("default");
+
+ useEffect(() => {
+ LocalStorage.getItem("selected_stores").then((s) =>
+ setSelectedStores(s ? JSON.parse(s) : ["all"]),
+ );
+ LocalStorage.getItem("saved_itad_games").then((s) =>
+ s ? setSavedGames(JSON.parse(s)) : setIsLoading(false),
+ );
+ LocalStorage.getItem("last_seen_prices").then(
+ (s) => s && setReferencePrices(JSON.parse(s)),
+ );
+ }, [refreshKey]);
+
+ const fetchPrices = async () => {
+ if (savedGames.length === 0) {
+ setIsLoading(false);
+ return;
+ }
+ setIsLoading(true);
+ try {
+ const cachedData = cache.get(CACHE_KEY);
+ if (cachedData) {
+ const parsed = JSON.parse(cachedData);
+ if (Date.now() - parsed.timestamp < CACHE_TTL) {
+ setRawPrices(parsed.rawPrices);
+ setBundleCounts(parsed.bundleCounts);
+ setIsLoading(false);
+ return;
+ }
+ }
+
+ const gameIds = savedGames.map((g) => g.id);
+ const [pRes, oRes] = await Promise.all([
+ fetch(
+ `https://api.isthereanydeal.com/games/prices/v2?key=${API_KEY}&country=${COUNTRY}&nondeals=true`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(gameIds),
+ },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/overview/v2?key=${API_KEY}&country=${COUNTRY}`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(gameIds),
+ },
+ ),
+ ]);
+ const [pJson, oJson] = await Promise.all([pRes.json(), oRes.json()]);
+
+ const rMap: any = {};
+ const newLastSeen: Record = {};
+
+ (Array.isArray(pJson) ? pJson : Object.values(pJson)).forEach(
+ (it: any) => {
+ rMap[it.id] = it.deals || [];
+ const bestDeal = it.deals?.find((d: any) =>
+ isStoreAllowed(d.shop?.name || "", selectedStores),
+ );
+ if (bestDeal?.price?.amount != null) {
+ newLastSeen[it.id] = bestDeal.price.amount;
+ }
+ },
+ );
+
+ const now = Date.now();
+ const bMap: any = {};
+ gameIds.forEach((id) => {
+ bMap[id] = (oJson.bundles || []).filter((b: any) => {
+ const isNotExpired = !b.expiry || new Date(b.expiry).getTime() > now;
+ return (
+ isNotExpired &&
+ b.tiers?.some((t: any) => t.games?.some((gm: any) => gm.id === id))
+ );
+ }).length;
+ });
+
+ setRawPrices(rMap);
+ setBundleCounts(bMap);
+
+ if (Object.keys(rMap).length > 0) {
+ cache.set(
+ CACHE_KEY,
+ JSON.stringify({
+ timestamp: Date.now(),
+ rawPrices: rMap,
+ bundleCounts: bMap,
+ }),
+ );
+ }
+
+ const existing = await LocalStorage.getItem("last_seen_prices");
+ const parsed = existing ? JSON.parse(existing) : {};
+ const merged = { ...parsed, ...newLastSeen };
+
+ await LocalStorage.setItem("last_seen_prices", JSON.stringify(merged));
+ } catch (e: any) {
+ if (e.name !== "AbortError" && !e.message?.includes("aborted"))
+ console.error(e);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchPrices();
+ }, [savedGames]);
+
+ const prices = useMemo(() => {
+ const map: any = {};
+ Object.keys(rawPrices).forEach((id) => {
+ if (!rawPrices[id] || !Array.isArray(rawPrices[id])) {
+ map[id] = null;
+ return;
+ }
+ map[id] =
+ rawPrices[id].filter((d: any) =>
+ isStoreAllowed(d.shop?.name || "", selectedStores),
+ )[0] || null;
+ });
+ return map;
+ }, [rawPrices, selectedStores]);
+
+ const scoreGame = (id: string) => {
+ const deal = prices[id];
+ if (!deal) return -999;
+ const price = deal.price?.amount ?? 999;
+ const cut = deal.cut ?? 0;
+ const bundle = bundleCounts[id] ?? 0;
+ const normDiscount = cut / 100;
+ const normPrice = 1 - Math.min(price / 60, 1);
+ const bundleBonus = bundle > 0 ? 0.15 : 0;
+ return normDiscount * 0.5 + normPrice * 0.35 + bundleBonus;
+ };
+
+ const sortedAndFilteredGames = useMemo(() => {
+ let list = [...savedGames];
+ if (filterMode === "deals") {
+ list = list.filter((g) => prices[g.id] && prices[g.id].cut > 0);
+ } else if (filterMode === "discount") {
+ list.sort((a, b) => (prices[b.id]?.cut || 0) - (prices[a.id]?.cut || 0));
+ } else if (filterMode === "opportunity") {
+ list.sort((a, b) => scoreGame(b.id) - scoreGame(a.id));
+ } else if (filterMode === "lowest") {
+ list.sort((a, b) => {
+ const pA = prices[a.id]?.price?.amount ?? 999999;
+ const pB = prices[b.id]?.price?.amount ?? 999999;
+ return pA - pB;
+ });
+ }
+ return list;
+ }, [savedGames, prices, filterMode]);
+
+ const removeGame = async (id: string) => {
+ const newList = savedGames.filter((g) => g.id !== id);
+ setSavedGames(newList);
+ await LocalStorage.setItem("saved_itad_games", JSON.stringify(newList));
+ cache.remove(CACHE_KEY);
+ };
+
+ const majorDrops = savedGames.filter((game) => {
+ const last = referencePrices[game.id];
+ const current = prices[game.id]?.price?.amount;
+ if (!last || !current) return false;
+ const diff = ((current - last) / last) * 100;
+ return diff <= -10;
+ });
+
+ return (
+
+
+
+
+
+
+
+ }
+ >
+ {savedGames.length === 0 && !isLoading ? (
+
+ ) : (
+ <>
+ {majorDrops.length > 0 && filterMode === "default" && (
+
+ )}
+ {majorDrops.length > 0 && filterMode === "default" && (
+
+ {sortedAndFilteredGames
+ .filter((g) => majorDrops.some((d) => d.id === g.id))
+ .map((game) => {
+ const deal = prices[game.id];
+ const diff =
+ ((deal?.price?.amount - referencePrices[game.id]) /
+ referencePrices[game.id]) *
+ 100;
+ return (
+
+ removeGame(game.id)}
+ />
+ }
+ icon={Icon.Sidebar}
+ />
+
+ }
+ />
+ );
+ })}
+
+ )}
+ {sortedAndFilteredGames
+ .filter(
+ (g) =>
+ filterMode !== "default" ||
+ !majorDrops.some((d) => d.id === g.id),
+ )
+ .map((game) => {
+ const deal = prices[game.id];
+ const acc = [];
+
+ if (!deal && isLoading) {
+ acc.push({
+ icon: Icon.Clock,
+ tooltip: "Loading price...",
+ tintColor: Color.SecondaryText,
+ });
+ } else if (deal) {
+ const currentPrice = deal.price?.amount;
+ const lastPrice = referencePrices[game.id];
+
+ if (lastPrice && currentPrice !== lastPrice) {
+ const diffAbs = currentPrice - lastPrice;
+ const diffPct = (diffAbs / lastPrice) * 100;
+
+ if (Math.abs(diffPct) >= 3) {
+ let label = "";
+ if (diffPct <= -10) label = "๐ฅ DROP";
+ else if (diffPct < 0) label = "โฌ DOWN";
+ else if (diffPct >= 10) label = "โ ๏ธ SPIKE";
+ else label = "โฌ UP";
+
+ acc.push({
+ tag: {
+ value: `${label} ${diffPct > 0 ? "+" : ""}${diffPct.toFixed(0)}%`,
+ color: diffPct > 0 ? Color.Red : Color.Green,
+ },
+ });
+ }
+ }
+
+ const regularPrice = deal.regular?.amount;
+ const currency = deal.price?.currency;
+ const cut = deal.cut || 0;
+
+ if (
+ cut > 0 &&
+ regularPrice != null &&
+ regularPrice > currentPrice
+ ) {
+ acc.push({
+ text: `${formatPrice(regularPrice, currency)} โ ${formatPrice(currentPrice, currency)}`,
+ });
+ } else {
+ acc.push({ text: formatPrice(currentPrice, currency) });
+ }
+
+ if (cut > 0) {
+ acc.push({ tag: { value: `-${cut}%`, color: Color.Green } });
+ }
+ }
+
+ if (bundleCounts[game.id] > 0) {
+ acc.push({
+ icon: { source: Icon.Box, tintColor: Color.Purple },
+ tooltip: "Available in a Bundle",
+ });
+ }
+ const isMusic =
+ (!game.type || game.type === "dlc" || game.type === "OTHER") &&
+ (game.title?.toLowerCase().endsWith(" ost") ||
+ game.title?.toLowerCase().includes("soundtrack"));
+ const cleanType = isMusic
+ ? "SOUNDTRACK"
+ : game.type === "game" || game.type === "base"
+ ? undefined
+ : game.type?.toUpperCase() || undefined;
+
+ let listIcon: any = Icon.Star;
+ if (deal?.cut >= 80) {
+ listIcon = { source: Icon.Star, tintColor: Color.Red };
+ } else if (deal?.cut >= 50) {
+ listIcon = { source: Icon.Star, tintColor: Color.Orange };
+ } else if (deal?.cut > 0) {
+ listIcon = { source: Icon.Star, tintColor: Color.Green };
+ }
+
+ return (
+
+
+ removeGame(game.id)}
+ />
+ }
+ icon={Icon.Sidebar}
+ />
+ {deal?.url && (
+
+ )}
+
+
+ removeGame(game.id)}
+ icon={Icon.Trash}
+ style={Action.Style.Destructive}
+ shortcut={{
+ Windows: { modifiers: ["ctrl"], key: "s" },
+ macOS: { modifiers: ["cmd"], key: "s" },
+ }}
+ />
+ {
+ setSavedGames([]);
+ await LocalStorage.setItem(
+ "saved_itad_games",
+ JSON.stringify([]),
+ );
+ cache.remove(CACHE_KEY);
+ }}
+ icon={Icon.Trash}
+ style={Action.Style.Destructive}
+ shortcut={{
+ Windows: {
+ modifiers: ["ctrl", "shift"],
+ key: "backspace",
+ },
+ macOS: {
+ modifiers: ["cmd", "shift"],
+ key: "backspace",
+ },
+ }}
+ />
+
+
+ }
+ />
+ );
+ })}
+ >
+ )}
+
+ );
+}
+
+function GameDetail({
+ gameId,
+ gameTitle,
+ gameSlug,
+ gameType,
+ removeGame,
+}: any) {
+ const [data, setData] = useState({
+ steamData: null,
+ realBundles: [],
+ deals: [],
+ historyLow: null,
+ overview: null,
+ historyChart: [],
+ lastChecked: null,
+ });
+ const [isLoading, setIsLoading] = useState(true);
+ const [refreshKey, setRefreshKey] = useState(0);
+ const [range, setRange] = useState<"3m" | "6m" | "1y">("1y");
+ const SHOW_CHART = preferences.showPriceHistoryChart ?? true;
+ const [selectedStores, setSelectedStores] = useState(["all"]);
+
+ useEffect(() => {
+ LocalStorage.getItem("selected_stores").then((s) =>
+ setSelectedStores(s ? JSON.parse(s) : ["all"]),
+ );
+ LocalStorage.getItem("preferred_chart_range").then(
+ (s) => s && setRange(s as any),
+ );
+ }, [refreshKey]);
+
+ const handleSetRange = (r: "3m" | "6m" | "1y") => {
+ setRange(r);
+ LocalStorage.setItem("preferred_chart_range", r);
+ };
+
+ useEffect(() => {
+ let isMounted = true;
+ const abort = new AbortController();
+ const detailCacheKey = `search_detail_${gameId}_${COUNTRY}_v1`;
+ const fetchDetailData = async () => {
+ setIsLoading(true);
+ const cached = cache.get(detailCacheKey);
+ if (cached) {
+ const parsed = JSON.parse(cached);
+ if (Date.now() - parsed.timestamp < DETAIL_CACHE_TTL) {
+ if (isMounted) {
+ setData({ ...parsed.data, lastChecked: parsed.timestamp });
+ setIsLoading(false);
+ }
+ return;
+ }
+ }
+ try {
+ const searchRes = await fetch(
+ `https://store.steampowered.com/api/storesearch/?term=${encodeURIComponent(gameTitle)}&l=english&cc=US`,
+ { signal: abort.signal },
+ );
+ const searchJson = await searchRes.json();
+ const targetItem =
+ searchJson?.items?.find(
+ (i: any) => i.name.toLowerCase() === gameTitle.toLowerCase(),
+ ) || searchJson?.items?.[0];
+ let steamData = null;
+ if (targetItem?.id) {
+ const detailRes = await fetch(
+ `https://store.steampowered.com/api/appdetails?appids=${targetItem.id}&l=english`,
+ { signal: abort.signal },
+ );
+ steamData = (await detailRes.json())?.[targetItem.id]?.data || null;
+ }
+ const fetchPromises = [
+ fetch(
+ `https://api.isthereanydeal.com/games/bundles/v2?key=${API_KEY}&id=${gameId}`,
+ { signal: abort.signal },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/prices/v2?key=${API_KEY}&country=${COUNTRY}&nondeals=true`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify([gameId]),
+ signal: abort.signal,
+ },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/historylow/v1?key=${API_KEY}&country=${COUNTRY}`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify([gameId]),
+ signal: abort.signal,
+ },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/overview/v2?key=${API_KEY}&country=${COUNTRY}`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify([gameId]),
+ signal: abort.signal,
+ },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/history/v2?key=${API_KEY}&id=${gameId}&country=${COUNTRY}`,
+ { signal: abort.signal },
+ ),
+ ];
+ const jsons = await Promise.all(
+ (await Promise.all(fetchPromises)).map((r) => r.json()),
+ );
+ const combined = {
+ steamData,
+ realBundles: Array.isArray(jsons[0])
+ ? jsons[0]
+ : jsons[0]?.[gameId]?.bundles || [],
+ deals:
+ (Array.isArray(jsons[1])
+ ? jsons[1][0]?.deals
+ : jsons[1]?.[gameId]?.deals) || [],
+ historyLow:
+ (Array.isArray(jsons[2])
+ ? jsons[2][0]?.low
+ : jsons[2]?.[gameId]?.low) || null,
+ overview: Array.isArray(jsons[3]) ? jsons[3][0] : jsons[3],
+ historyChart: Array.isArray(jsons[4]) ? jsons[4] : [],
+ };
+ if (isMounted) {
+ cache.set(
+ detailCacheKey,
+ JSON.stringify({ timestamp: Date.now(), data: combined }),
+ );
+ setData({ ...combined, lastChecked: Date.now() });
+ }
+ } catch (e: any) {
+ if (e.name !== "AbortError" && !e.message?.includes("aborted"))
+ console.error(e);
+ } finally {
+ if (isMounted) setIsLoading(false);
+ }
+ };
+ fetchDetailData();
+ return () => {
+ isMounted = false;
+ abort.abort();
+ };
+ }, [gameId, gameTitle, SHOW_CHART, refreshKey]);
+
+ const {
+ steamData,
+ realBundles,
+ deals = [],
+ historyLow,
+ overview,
+ historyChart,
+ lastChecked,
+ } = data;
+
+ // โฑ๏ธ TIME DOMAIN: Single deterministic snapshot
+ const now = useMemo(() => Date.now(), [refreshKey]);
+
+ // ๐ฆ BUNDLE DOMAIN: Single source of truth
+ const bundle = useMemo(() => {
+ const isBundleActive = (b: any) => {
+ if (!b?.expiry) return true;
+ const t = new Date(b.expiry).getTime();
+ return Number.isFinite(t) && t > now;
+ };
+
+ const activeBundles = realBundles.filter(isBundleActive);
+ const activeCount = activeBundles.length;
+
+ const recentBundles = realBundles.filter((b: any) => {
+ const tsRaw = b.created ?? b.timestamp;
+ const ts = tsRaw ? new Date(tsRaw).getTime() : null;
+ return ts && ts < now && now - ts < RECENT_BUNDLE_WINDOW;
+ });
+
+ const totalBundles =
+ realBundles?.length > 0
+ ? realBundles.length
+ : typeof overview?.bundles === "number"
+ ? overview.bundles
+ : overview?.bundles?.count || overview?.bundles?.length || 0;
+
+ const allBundlesForTs = Array.isArray(overview?.bundles)
+ ? overview.bundles
+ : realBundles;
+
+ const timestamps = allBundlesForTs
+ .map((b: any) => {
+ const ts = b.created ?? b.timestamp ?? b.publish ?? b.expiry;
+ return ts ? new Date(ts).getTime() : null;
+ })
+ .filter((t: any) => t !== null && !isNaN(t));
+
+ const lastBundleTs =
+ timestamps.length > 0 ? Math.max(...timestamps) : undefined;
+ const lastBundleDate = lastBundleTs ? new Date(lastBundleTs) : null;
+ const lastBundleAgo = lastBundleTs ? now - lastBundleTs : Infinity;
+
+ const ONE_YEAR = 365 * 24 * 60 * 60 * 1000;
+
+ let state: string | null = null;
+ let icon: Image.ImageLike | undefined = undefined;
+ let color: Color | undefined = undefined;
+
+ if (activeCount > 0) {
+ state = "Active";
+ icon = Icon.Box;
+ color = Color.Purple;
+ } else if (totalBundles === 0) {
+ state = "Never Bundled";
+ icon = Icon.XMarkCircle;
+ color = Color.SecondaryText;
+ } else if (recentBundles.length >= 4 && lastBundleAgo < ONE_YEAR) {
+ state = "Frequent";
+ icon = Icon.Repeat;
+ color = Color.Orange;
+ } else if (totalBundles >= 2 && lastBundleAgo < ONE_YEAR) {
+ state = "Occasional";
+ icon = Icon.Circle;
+ color = Color.SecondaryText;
+ } else if (totalBundles === 1 && lastBundleAgo < ONE_YEAR) {
+ state = "Bundled recently";
+ icon = Icon.Circle;
+ color = Color.SecondaryText;
+ } else if (lastBundleDate) {
+ const month = lastBundleDate.toLocaleString("en-US", { month: "short" });
+ const year = lastBundleDate.getFullYear();
+ state = `Last bundled ${month} ${year}`;
+ icon = Icon.Clock;
+ color = Color.SecondaryText;
+ } else if (totalBundles > 0) {
+ state = `Bundled ${totalBundles} times`;
+ icon = Icon.Clock;
+ color = Color.SecondaryText;
+ }
+
+ const getLowestPrice = (bundle: any) => {
+ const prices = bundle.tiers
+ ?.map((t: any) => t.price?.amount)
+ .filter((p: number | undefined) => typeof p === "number");
+ return prices?.length ? Math.min(...prices) : Infinity;
+ };
+
+ const featuredBundle =
+ activeBundles.length > 0
+ ? activeBundles.reduce(
+ (best: any, current: any) =>
+ getLowestPrice(current) < getLowestPrice(best) ? current : best,
+ activeBundles[0],
+ )
+ : null;
+
+ const featuredPrice = featuredBundle
+ ? getLowestPrice(featuredBundle)
+ : null;
+
+ const getGameTierPrice = (b: any) => {
+ const tiersWithGame = b.tiers?.filter((t: any) =>
+ t.games?.some((gm: any) => gm.id === gameId),
+ );
+ if (tiersWithGame && tiersWithGame.length > 0) {
+ const prices = tiersWithGame
+ .map((t: any) => t.price?.amount)
+ .filter((p: any) => typeof p === "number");
+ return prices.length > 0 ? Math.min(...prices) : Infinity;
+ }
+ return Infinity;
+ };
+
+ const bestGameTierPrice =
+ activeBundles.length > 0
+ ? Math.min(...activeBundles.map(getGameTierPrice))
+ : null;
+
+ const actualBundlePrice =
+ bestGameTierPrice !== Infinity && bestGameTierPrice !== null
+ ? bestGameTierPrice
+ : featuredPrice;
+
+ return {
+ activeBundles,
+ activeCount,
+ recentBundles,
+ totalBundles,
+ state,
+ icon,
+ color,
+ featuredBundle,
+ featuredPrice,
+ actualBundlePrice,
+ getLowestPrice,
+ };
+ }, [realBundles, overview, now, gameId]);
+
+ const allowedHistory = useMemo(() => {
+ return (historyChart || []).filter(
+ (pt: any) =>
+ pt.deal?.price?.amount != null &&
+ isStoreAllowed(pt.shop?.name || "", selectedStores),
+ );
+ }, [historyChart, selectedStores]);
+
+ const filteredDeals = deals.filter((d: any) =>
+ isStoreAllowed(d.shop?.name || "", selectedStores),
+ );
+ const currentBest = filteredDeals?.[0];
+ const currentPrice = currentBest?.price?.amount;
+
+ const bundleValue = useMemo(() => {
+ if (!bundle.activeBundles.length || currentPrice == null) return null;
+
+ for (const b of bundle.activeBundles) {
+ let gameTierIndex = -1;
+ b.tiers?.forEach((t: any, i: number) => {
+ if (
+ t.games?.some((gm: any) => gm.id === gameId || gm.name === gameTitle)
+ ) {
+ gameTierIndex = i;
+ }
+ });
+
+ if (gameTierIndex === -1) continue;
+
+ const tierPrice = b.tiers[gameTierIndex]?.price?.amount;
+ if (!tierPrice) continue;
+
+ if (tierPrice < currentPrice) {
+ return {
+ type: "better",
+ message: "Cheaper in active bundle",
+ tier: b.tiers[gameTierIndex],
+ bundle: b,
+ };
+ }
+
+ let totalGames = 0;
+ for (let i = 0; i <= gameTierIndex; i++) {
+ totalGames += b.tiers[i]?.games?.length || 0;
+ }
+
+ const unitPrice = totalGames > 0 ? tierPrice / totalGames : tierPrice;
+ if (unitPrice < currentPrice) {
+ return {
+ type: "value",
+ message: "Better value in bundle",
+ tier: b.tiers[gameTierIndex],
+ bundle: b,
+ };
+ }
+ }
+ return null;
+ }, [bundle.activeBundles, currentPrice, gameId, gameTitle]);
+
+ const allTimeLow = historyLow?.price?.amount ?? historyLow?.amount;
+ const hCurrency =
+ historyLow?.price?.currency ?? historyLow?.currency ?? "USD";
+
+ // ๐งฎ SCORE DOMAIN: Untouched heuristic engine
+ const twelveMonthTime = now - 365 * 24 * 60 * 60 * 1000;
+ const statsPrices = allowedHistory
+ .filter((pt: any) => new Date(pt.timestamp).getTime() >= twelveMonthTime)
+ .map((pt: any) => pt.deal.price.amount);
+
+ let typicalMin: number | null = null;
+ let typicalMax: number | null = null;
+ let median: number | null = null;
+
+ if (statsPrices.length > 0) {
+ const sorted = [...statsPrices].sort((a, b) => a - b);
+ const filtered = sorted.slice(
+ Math.floor(sorted.length * 0.1),
+ Math.floor(sorted.length * 0.9) + 1,
+ );
+ if (filtered.length > 0) {
+ typicalMin = Math.min(...filtered);
+ typicalMax = Math.max(...filtered);
+ const mid = Math.floor(filtered.length / 2);
+ median =
+ filtered.length % 2 !== 0
+ ? filtered[mid]
+ : (filtered[mid - 1] + filtered[mid]) / 2;
+ }
+ }
+
+ const cut = currentBest?.cut || 0;
+ let verdict = "";
+ let reason: string | undefined;
+ let recommendation = "";
+
+ const mapUI = (v: string) => {
+ switch (v) {
+ case "Free":
+ return { badge: "free", color: Color.Blue, icon: Icon.Gift };
+ case "Strong deal":
+ return { badge: "best", color: Color.Green, icon: Icon.Star };
+ case "Good deal":
+ return { badge: "good", color: Color.Green, icon: Icon.ThumbsUp };
+ case "Fair price":
+ return {
+ badge: "neutral",
+ color: Color.SecondaryText,
+ icon: Icon.Minus,
+ };
+ case "Not great":
+ case "Not ideal":
+ return { badge: "weak", color: Color.Orange, icon: Icon.Clock };
+ case "Overpriced":
+ return { badge: "bad", color: Color.Red, icon: Icon.XMarkCircle };
+ default:
+ return {
+ badge: "neutral",
+ color: Color.SecondaryText,
+ icon: Icon.Minus,
+ };
+ }
+ };
+
+ if (currentPrice === 0 || cut === 100) {
+ recommendation = "๐ FREE";
+ verdict = "Free";
+ reason = "Free to claim";
+ } else if (bundle.activeCount > 0 && bundleValue?.type === "better") {
+ recommendation = "๐ด HIGH PRICE";
+ verdict = "Overpriced";
+ reason = bundleValue.message;
+ } else if (bundle.activeCount > 0 && bundleValue?.type === "value") {
+ recommendation = "๐ก FAIR PRICE";
+ verdict = "Not ideal";
+ reason = bundleValue.message;
+ } else if (currentPrice != null) {
+ let score = 0;
+ const safeATL =
+ allTimeLow && allTimeLow > 0 ? allTimeLow : currentPrice || 1;
+ const ratioATL = currentPrice / safeATL;
+
+ if (ratioATL <= 0.95) score += 0.35;
+ else if (ratioATL <= 1.05) score += 0.25;
+ else if (ratioATL <= 1.2) score += 0.1;
+ else if (ratioATL >= 2) score -= 0.2;
+
+ if (median != null && median > 0) {
+ const ratioMedian = currentPrice / median;
+ if (ratioMedian <= 0.75) score += 0.25;
+ else if (ratioMedian <= 0.9) score += 0.15;
+ else if (ratioMedian >= 1.25) score -= 0.2;
+ }
+
+ if (cut >= 75 && ratioATL <= 1.2) score += 0.4;
+ else if (cut >= 75) score += 0.3;
+ else if (cut >= 50) score += 0.2;
+ else if (cut >= 25) score += 0.1;
+ else if (cut > 0) score += 0.05;
+
+ score = Math.max(0, Math.min(1, score));
+
+ // Recommendation thresholds
+ if (score >= 0.7) recommendation = "๐ฅ GREAT DEAL";
+ else if (score >= 0.5) recommendation = "๐ GOOD DEAL";
+ else if (score >= 0.35) recommendation = "๐ก FAIR PRICE";
+ else recommendation = "๐ด HIGH PRICE";
+
+ const isATL = currentPrice <= safeATL;
+ const isNearATL = currentPrice <= safeATL * 1.05;
+ const isBelowAvg = median && currentPrice < median * 0.85;
+ const isAtTypical = median && currentPrice <= median * 1.05;
+
+ if (score < 0.35) {
+ if (cut === 0 && (!median || isAtTypical)) {
+ verdict = "Fair price";
+ reason = "Typical price for this game";
+ recommendation = "๐ก FAIR PRICE";
+ } else {
+ verdict = cut > 0 ? "Not ideal" : "Overpriced";
+ reason =
+ cut > 0 ? "Discounted, but still high" : "Above usual price range";
+ }
+ } else if (score < 0.5) {
+ verdict = "Not ideal";
+ if (bundle.state === "Frequent") reason = "Frequently bundled, wait";
+ else if (cut >= 70) reason = "Big discount, not lowest";
+ else
+ reason = cut > 0 ? "Small discount, better wait" : "No discount, wait";
+ } else {
+ if (isATL) {
+ verdict = score >= 0.7 ? "Strong deal" : "Good deal";
+ reason = cut > 0 ? "At all-time low price" : "Lowest recorded price";
+ } else if (isNearATL) {
+ verdict = "Good deal";
+ reason = "Near all-time low price";
+ } else if (cut >= 75) {
+ verdict = "Good deal";
+ reason = "Large discount applied";
+ } else if (isBelowAvg) {
+ verdict = "Good deal";
+ reason = "Well below usual price";
+ } else {
+ verdict = "Fair price";
+ reason = "Decent price, not the lowest";
+ }
+ }
+ }
+
+ if (!reason) {
+ reason = "Typical pricing";
+ }
+
+ const plotData: any[] = [];
+ const cutoffTime =
+ now -
+ (range === "3m" ? 90 : range === "6m" ? 180 : 365) * 24 * 60 * 60 * 1000;
+ if (allowedHistory.length > 0) {
+ allowedHistory
+ .filter((pt: any) => new Date(pt.timestamp).getTime() >= cutoffTime)
+ .reverse()
+ .forEach((pt: any) => {
+ plotData.push({
+ x: new Date(pt.timestamp).toISOString().split("T")[0],
+ y: pt.deal.price.amount,
+ });
+ });
+ }
+
+ let chartUrl = "";
+ if (SHOW_CHART && plotData.length > 0) {
+ const minY = Math.min(...plotData.map((p) => p.y));
+ const datasets: any[] = [
+ {
+ data: plotData,
+ borderColor: "#2ecc71",
+ backgroundColor: "rgba(46, 204, 113, 0.05)",
+ steppedLine: true,
+ fill: true,
+ pointRadius: plotData.map((p) => (Math.abs(p.y - minY) < 0.01 ? 4 : 0)),
+ pointBackgroundColor: plotData.map((p) =>
+ Math.abs(p.y - minY) < 0.01 ? "#e74c3c" : "transparent",
+ ),
+ pointBorderColor: plotData.map((p) =>
+ Math.abs(p.y - minY) < 0.01 ? "#ffffff" : "transparent",
+ ),
+ pointBorderWidth: 2,
+ borderWidth: 2,
+ },
+ ];
+
+ if (median !== null) {
+ datasets.push({
+ data: plotData.map((p) => ({ x: p.x, y: median })),
+ borderColor: "rgba(255, 255, 255, 0.2)",
+ borderWidth: 1,
+ borderDash: [5, 5],
+ fill: false,
+ pointRadius: 0,
+ });
+ }
+
+ const config: any = {
+ type: "line",
+ data: { datasets },
+ options: {
+ layout: { padding: { right: 30, left: 5, top: 10, bottom: 5 } },
+ legend: { display: false },
+ scales: {
+ xAxes: [
+ {
+ type: "time",
+ time: {
+ parser: "YYYY-MM-DD",
+ unit: "month",
+ displayFormats: { month: "MMM YY" },
+ },
+ gridLines: { color: "rgba(255, 255, 255, 0.1)" },
+ ticks: { maxRotation: 0, maxTicksLimit: 6, fontSize: 8 },
+ },
+ ],
+ yAxes: [
+ {
+ gridLines: { color: "rgba(255, 255, 255, 0.1)" },
+ ticks: { beginAtZero: true, fontSize: 8 },
+ },
+ ],
+ },
+ },
+ };
+ chartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(config))}&w=250&h=110&devicePixelRatio=2&bkg=transparent`;
+ }
+
+ const isDiscounted = currentBest && currentBest.cut > 0;
+ let saleTagText = "";
+ if (isDiscounted) {
+ if (currentBest.cut >= 70) saleTagText = "MEGA SALE";
+ else if (currentBest.cut >= 40) saleTagText = "ON SALE";
+ else saleTagText = "DISCOUNT";
+ }
+
+ const heroSection =
+ currentBest && currentPrice != null
+ ? `${recommendation || "Price Details"}
\n${formatPrice(currentPrice, currentBest.price?.currency)} ${isDiscounted ? `-${currentBest.cut}%` : ""} ยท ${currentBest.shop?.name}
\n\n---\n\n`
+ : "";
+
+ const markdown = `
+${steamData?.header_image ? `
\n\n` : ""}
+# ${gameTitle}
+${
+ steamData?.genres
+ ? `*${steamData.genres
+ .map((g: any) => g.description)
+ .slice(0, 2)
+ .join(
+ ", ",
+ )}*${steamData?.release_date?.date ? ` ยท ${new Date(steamData.release_date.date).getFullYear()}` : ""}`
+ : ""
+}
+
+${steamData?.short_description ? `> ${steamData.short_description.replace(/<[^>]*>?/gm, "").split(". ")[0]}.` : ""}
+
+${heroSection}
+๐ฐ **Prices in ${COUNTRY}**
+
+| Store | Price | RRP | Discount |
+| :--- | :--- | :--- | :--- |
+${filteredDeals?.length ? filteredDeals.map((p: any) => `| ${p.url ? `[${p.shop?.name}](${p.url})` : p.shop?.name} | **${formatPrice(p.price?.amount, p.price?.currency)}** | ${formatPrice(p.regular?.amount, p.price?.currency)} | ${p.cut > 0 ? "-" + p.cut + "%" : "-"} |`).join("\n") : "| No data found | - | - | - |"}
+
+${chartUrl ? `\n---\n\n๐ **Trend: ${range === "1y" ? "12 Months" : range === "6m" ? "6 Months" : "3 Months"}**\n\n\n` : ""}
+`;
+
+ return (
+
+ {recommendation && (
+
+ )}
+ {verdict &&
+ ["๐ฅ GREAT DEAL", "๐ GOOD DEAL", "๐ FREE"].includes(
+ recommendation,
+ ) && (
+
+ )}
+ {reason && (
+ 28 ? reason.slice(0, 25) + "..." : reason}
+ />
+ )}
+ {(isDiscounted || bundle.activeCount > 0) && (
+ <>
+
+
+ {isDiscounted && (
+
+ )}
+ {bundle.activeCount > 0 && (
+
+ )}
+
+ >
+ )}
+
+
+ {typicalMin !== null &&
+ typicalMax !== null &&
+ typicalMin !== typicalMax && (
+
+ )}
+ {median !== null && (
+
+ )}
+
+ {bundle.state && (
+ <>
+
+
+ >
+ )}
+ {bundleValue?.tier && bundleValue?.bundle && (
+
+ )}
+
+
+ = 23
+ ? "All Stores"
+ : `${selectedStores.length} Selected`
+ }
+ />
+
+
+
+
+ {gameSlug && (
+
+ )}
+ {steamData?.steam_appid && (
+
+ )}
+ {lastChecked && (
+ <>
+
+
+ >
+ )}
+
+ }
+ actions={
+
+
+ {currentBest?.url && (
+
+ )}
+
+ {currentBest?.url && (
+
+ )}
+
+ handleSetRange("3m")}
+ icon={range === "3m" ? Icon.Checkmark : Icon.Circle}
+ />
+ handleSetRange("6m")}
+ icon={range === "6m" ? Icon.Checkmark : Icon.Circle}
+ />
+ handleSetRange("1y")}
+ icon={range === "1y" ? Icon.Checkmark : Icon.Circle}
+ />
+
+ {
+ const c = new Cache();
+ c.remove(`search_detail_${gameId}_${COUNTRY}_v1`);
+ setIsLoading(true);
+ setRefreshKey((k) => k + 1);
+ }}
+ />
+ {realBundles.length > 0 && (
+
+ }
+ icon={Icon.Box}
+ shortcut={{
+ Windows: { modifiers: ["ctrl"], key: "b" },
+ macOS: { modifiers: ["cmd"], key: "b" },
+ }}
+ />
+ )}
+ {removeGame && (
+
+ )}
+
+
+ }
+ />
+ );
+}
+
+function BundleContentViewer({ bundles, gameTitle }: any) {
+ const firstBundleUrl = cleanBundleUrl(
+ bundles?.[0]?.url || bundles?.[0]?.details,
+ );
+
+ let markdown = `# ๐ฆ Bundle Contents for ${gameTitle}\n\n`;
+ bundles.forEach((b: any, i: number) => {
+ const active = b.expiry ? new Date(b.expiry) > new Date() : true;
+ markdown += `## ${active ? "โ
" : "โ"} ${b.title || `Bundle ${i + 1}`}\n**Page:** ${b.page?.name || "Unknown"}${b.expiry ? ` | **Expires:** ${new Date(b.expiry).toLocaleDateString("en-GB")}` : ""}\n${b.note ? `\n> ${b.note}` : ""}\n\n`;
+ b.tiers?.forEach((t: any, ti: number) => {
+ markdown += `### ${t.name || `Tier ${ti + 1}`} - **${t.price ? formatPrice(t.price.amount, t.price.currency) : "N/A"}**\n`;
+ t.games?.forEach(
+ (g: any) => (markdown += `- ${g.title || g.name || g}\n`),
+ );
+ markdown += `\n`;
+ });
+ });
+ return (
+
+
+
+
+ ) : undefined
+ }
+ />
+ );
+}
diff --git a/extensions/game-scout/src/search-games.tsx b/extensions/game-scout/src/search-games.tsx
new file mode 100644
index 00000000000..7f3aef118b1
--- /dev/null
+++ b/extensions/game-scout/src/search-games.tsx
@@ -0,0 +1,1353 @@
+import { useEffect, useState, useMemo } from "react";
+import { useFetch } from "@raycast/utils";
+import {
+ List,
+ ActionPanel,
+ Action,
+ Icon,
+ Color,
+ Detail,
+ LocalStorage,
+ Cache,
+ getPreferenceValues,
+ openExtensionPreferences,
+ Image,
+} from "@raycast/api";
+
+const preferences = getPreferenceValues<{
+ itadApiKey: string;
+ country: string;
+ maxResults: string;
+ showMature: boolean;
+ showDLCGameSearch: boolean;
+ showPriceHistoryChart: boolean;
+}>();
+
+const API_KEY = (preferences.itadApiKey || "").trim();
+const COUNTRY = preferences.country;
+const MAX_RESULTS = parseInt(preferences.maxResults) || 25;
+
+const detailCache = new Cache({ namespace: "search_detail" });
+const DETAIL_CACHE_TTL = 6 * 60 * 60 * 1000;
+const RECENT_BUNDLE_WINDOW = 2 * 365 * 24 * 60 * 60 * 1000;
+
+import { formatPrice, isStoreAllowed } from "./utils";
+
+const cleanBundleUrl = (url?: string) => {
+ if (!url) return "";
+ if (url.includes("u=http")) {
+ try {
+ return decodeURIComponent(url.split("u=")[1].split("&")[0]);
+ } catch {
+ return url;
+ }
+ }
+ return url;
+};
+
+export default function Command() {
+ const [apiError, setApiError] = useState(false);
+ const isApiKeyValid = API_KEY.length > 0;
+ const isCountryValid = COUNTRY.length === 2;
+
+ if (!isApiKeyValid || !isCountryValid) {
+ return (
+
+
+
+
+ }
+ />
+
+ );
+ }
+
+ const [searchText, setSearchText] = useState("");
+ const [searchQuery, setSearchQuery] = useState("");
+ const [searchData, setSearchData] = useState([]);
+ const [loadingSearch, setLoadingSearch] = useState(false);
+ const [savedGames, setSavedGames] = useState<
+ { id: string; title: string; slug: string; type?: string }[]
+ >([]);
+
+ useEffect(() => {
+ LocalStorage.getItem("saved_itad_games").then(
+ (stored) => stored && setSavedGames(JSON.parse(stored)),
+ );
+ }, []);
+
+ const toggleSave = async (game: any) => {
+ let newList;
+ if (savedGames.some((g) => g.id === game.id)) {
+ newList = savedGames.filter((g) => g.id !== game.id);
+ } else {
+ newList = [
+ ...savedGames,
+ {
+ id: game.id,
+ title: game.title,
+ slug: game.slug,
+ type: game.type || "OTHER",
+ },
+ ];
+
+ const savedCache = new Cache();
+ savedCache.remove(`itad_saved_prices_v1_${COUNTRY}`);
+ }
+ setSavedGames(newList);
+ await LocalStorage.setItem("saved_itad_games", JSON.stringify(newList));
+ };
+
+ useEffect(() => {
+ if (!searchQuery) {
+ setSearchData([]);
+ return;
+ }
+ const fetchData = async () => {
+ setLoadingSearch(true);
+ try {
+ const res = await fetch(
+ `https://api.isthereanydeal.com/games/search/v1?key=${API_KEY}&title=${encodeURIComponent(searchQuery)}`,
+ );
+ if (res.status === 401 || res.status === 403) {
+ setApiError(true);
+ setLoadingSearch(false);
+ return;
+ }
+ const json = await res.json();
+ const results = Array.isArray(json)
+ ? json
+ : json.data || json.results || [];
+ const query = searchQuery.toLowerCase();
+
+ const score = (t: string) => {
+ const lower = t.toLowerCase();
+ if (lower === query) return 0;
+ if (lower.startsWith(query)) return 1;
+ if (lower.includes(query)) return 2;
+ return 3;
+ };
+ results.sort((a: any, b: any) => score(a.title) - score(b.title));
+
+ setSearchData(results);
+ } catch {
+ setSearchData([]);
+ }
+ setLoadingSearch(false);
+ };
+ fetchData();
+ }, [searchQuery]);
+
+ const gameIds = searchData?.slice(0, MAX_RESULTS).map((g: any) => g.id) || [];
+
+ const { data: priceData, isLoading: priceLoading } = useFetch(
+ `https://api.isthereanydeal.com/games/overview/v2?key=${API_KEY}&country=${COUNTRY}&nondeals=true`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(gameIds),
+ execute: gameIds.length > 0 && searchQuery.length > 0,
+ mapResult: (res: any) => ({
+ data: Array.isArray(res) ? res : Object.values(res).flat(),
+ }),
+ },
+ );
+
+ const bundleCounts = useMemo(() => {
+ const counts: Record = {};
+ const now = Date.now();
+
+ if (priceData) {
+ const bundles = (Array.isArray(priceData) ? priceData : []).filter(
+ (item: any) => {
+ const hasTiers = Array.isArray(item.tiers);
+ const isNotExpired =
+ !item.expiry || new Date(item.expiry).getTime() > now;
+ return hasTiers && isNotExpired;
+ },
+ );
+
+ bundles.forEach((b: any) => {
+ b.tiers?.forEach((t: any) => {
+ t.games?.forEach((gm: any) => {
+ if (gm.id) {
+ counts[gm.id] = (counts[gm.id] || 0) + 1;
+ }
+ });
+ });
+ });
+ }
+ return counts;
+ }, [priceData]);
+
+ const filteredData = searchData.filter((game: any) => {
+ if (!preferences.showMature && game.mature) return false;
+ if (!preferences.showDLCGameSearch && game.type === "dlc") return false;
+ return true;
+ });
+
+ const isTyping =
+ searchText.trim() !== searchQuery && searchText.trim().length > 0;
+
+ return (
+ {
+ setSearchText(t);
+ if (t.trim() === "") setSearchQuery("");
+ }}
+ searchBarPlaceholder="Search games (e.g. Elden Ring)..."
+ >
+ {apiError ? (
+
+
+
+ }
+ />
+ ) : searchQuery.length === 0 ? (
+
+ setSearchQuery(searchText.trim())}
+ icon={Icon.MagnifyingGlass}
+ />
+
+ }
+ />
+ ) : filteredData.length === 0 && !loadingSearch ? (
+
+ setSearchQuery(searchText.trim())}
+ icon={Icon.MagnifyingGlass}
+ />
+
+ }
+ />
+ ) : (
+ filteredData.slice(0, MAX_RESULTS).map((game: any) => {
+ const overview = Array.isArray(priceData)
+ ? priceData.find((p: any) => p.id === game.id)
+ : undefined;
+ const deal = overview?.current || overview;
+ const isSaved = savedGames.some((g) => g.id === game.id);
+
+ const accessories = [];
+ if (game.mature)
+ accessories.push({
+ tag: { value: "18+", color: Color.Red },
+ tooltip: "Mature Content",
+ });
+
+ if (priceLoading && !deal) {
+ accessories.push({
+ icon: Icon.Clock,
+ tooltip: "Loading price...",
+ tintColor: Color.SecondaryText,
+ });
+ } else if (deal) {
+ const currentAmount = deal.price?.amount;
+ const regularAmount = deal.regular?.amount;
+ const currency = deal.price?.currency;
+ const cut = deal.cut || 0;
+
+ if (
+ cut > 0 &&
+ regularAmount != null &&
+ regularAmount > currentAmount
+ ) {
+ accessories.push({
+ text: `${formatPrice(regularAmount, currency)} โ ${formatPrice(currentAmount, currency)}`,
+ });
+ accessories.push({
+ tag: { value: `-${cut}%`, color: Color.Green },
+ });
+ } else {
+ accessories.push({ text: formatPrice(currentAmount, currency) });
+ }
+
+ if (
+ typeof bundleCounts !== "undefined" &&
+ bundleCounts[game.id] > 0
+ ) {
+ accessories.push({
+ icon: { source: Icon.Box, tintColor: Color.Purple },
+ tooltip: "Available in a Bundle",
+ });
+ }
+ }
+ const isMusic =
+ (game.type === null || game.type === "dlc") &&
+ (game.title?.toLowerCase().endsWith(" ost") ||
+ game.title?.toLowerCase().includes("soundtrack"));
+ const cleanType = isMusic
+ ? "SOUNDTRACK"
+ : game.type === "game" || game.type === "base"
+ ? undefined
+ : game.type?.toUpperCase() || undefined;
+
+ return (
+
+
+ {isTyping ? (
+ setSearchQuery(searchText.trim())}
+ icon={Icon.MagnifyingGlass}
+ />
+ ) : (
+ toggleSave(game)}
+ />
+ }
+ icon={Icon.Sidebar}
+ />
+ )}
+ toggleSave(game)}
+ icon={isSaved ? Icon.Trash : Icon.Star}
+ shortcut={{
+ Windows: { modifiers: ["ctrl"], key: "s" },
+ macOS: { modifiers: ["cmd"], key: "s" },
+ }}
+ style={
+ isSaved
+ ? Action.Style.Destructive
+ : Action.Style.Regular
+ }
+ />
+
+
+ }
+ />
+ );
+ })
+ )}
+
+ );
+}
+
+function GameDetail({
+ gameId,
+ gameTitle,
+ gameSlug,
+ gameType,
+ isSaved,
+ toggleSave,
+ removeGame,
+}: any) {
+ const [data, setData] = useState({
+ steamData: null,
+ realBundles: [],
+ deals: [],
+ historyLow: null,
+ overview: null,
+ historyChart: [],
+ lastChecked: null,
+ });
+ const [isLoading, setIsLoading] = useState(true);
+ const [range, setRange] = useState<"3m" | "6m" | "1y">("1y");
+ const SHOW_CHART = preferences.showPriceHistoryChart ?? true;
+ const [selectedStores, setSelectedStores] = useState(["all"]);
+ const [refreshKey, setRefreshKey] = useState(0);
+
+ useEffect(() => {
+ LocalStorage.getItem("selected_stores").then((s) =>
+ setSelectedStores(s ? JSON.parse(s) : ["all"]),
+ );
+ LocalStorage.getItem("preferred_chart_range").then(
+ (s) => s && setRange(s as any),
+ );
+ }, [refreshKey]);
+
+ const handleSetRange = (r: "3m" | "6m" | "1y") => {
+ setRange(r);
+ LocalStorage.setItem("preferred_chart_range", r);
+ };
+
+ useEffect(() => {
+ let isMounted = true;
+ const abort = new AbortController();
+ const detailCacheKey = `search_detail_${gameId}_${COUNTRY}_v1`;
+
+ const fetchDetailData = async () => {
+ setIsLoading(true);
+ const cached = detailCache.get(detailCacheKey);
+ if (cached) {
+ const parsed = JSON.parse(cached);
+ if (Date.now() - parsed.timestamp < DETAIL_CACHE_TTL) {
+ if (isMounted) {
+ setData({ ...parsed.data, lastChecked: parsed.timestamp });
+ setIsLoading(false);
+ }
+ return;
+ }
+ }
+
+ try {
+ const searchRes = await fetch(
+ `https://store.steampowered.com/api/storesearch/?term=${encodeURIComponent(gameTitle)}&l=english&cc=US`,
+ { signal: abort.signal },
+ );
+ const searchJson = await searchRes.json();
+
+ let targetItem = searchJson?.items?.find(
+ (item: any) => item.name.toLowerCase() === gameTitle.toLowerCase(),
+ );
+ if (!targetItem) {
+ targetItem = searchJson?.items?.find((item: any) => {
+ const sName = item.name.toLowerCase();
+ const iName = gameTitle.toLowerCase();
+ if (sName.includes(iName) || iName.includes(sName)) {
+ const sNums = sName.match(/\b\d+\b/g) || [];
+ const iNums = iName.match(/\b\d+\b/g) || [];
+ return sNums.every((n: string) => iNums.includes(n));
+ }
+ return false;
+ });
+ }
+ if (!targetItem) targetItem = searchJson?.items?.[0];
+
+ let steamData = null;
+ if (targetItem?.id) {
+ const detailRes = await fetch(
+ `https://store.steampowered.com/api/appdetails?appids=${targetItem.id}&l=english`,
+ { signal: abort.signal },
+ );
+ steamData = (await detailRes.json())?.[targetItem.id]?.data || null;
+ }
+
+ const fetchPromises = [
+ fetch(
+ `https://api.isthereanydeal.com/games/bundles/v2?key=${API_KEY}&id=${gameId}`,
+ { signal: abort.signal },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/prices/v2?key=${API_KEY}&country=${COUNTRY}&nondeals=true`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify([gameId]),
+ signal: abort.signal,
+ },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/historylow/v1?key=${API_KEY}&country=${COUNTRY}`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify([gameId]),
+ signal: abort.signal,
+ },
+ ),
+ fetch(
+ `https://api.isthereanydeal.com/games/overview/v2?key=${API_KEY}&country=${COUNTRY}`,
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify([gameId]),
+ signal: abort.signal,
+ },
+ ),
+ ];
+ if (SHOW_CHART)
+ fetchPromises.push(
+ fetch(
+ `https://api.isthereanydeal.com/games/history/v2?key=${API_KEY}&id=${gameId}&country=${COUNTRY}`,
+ { signal: abort.signal },
+ ),
+ );
+
+ const jsons = await Promise.all(
+ (await Promise.all(fetchPromises)).map((r) => r.json()),
+ );
+ const combined = {
+ steamData,
+ realBundles: Array.isArray(jsons[0])
+ ? jsons[0]
+ : jsons[0]?.[gameId]?.bundles || [],
+ deals:
+ (Array.isArray(jsons[1])
+ ? jsons[1][0]?.deals
+ : jsons[1]?.[gameId]?.deals) || [],
+ historyLow:
+ (Array.isArray(jsons[2])
+ ? jsons[2][0]?.low
+ : jsons[2]?.[gameId]?.low) || null,
+ overview: Array.isArray(jsons[3]) ? jsons[3][0] : jsons[3],
+ historyChart: Array.isArray(jsons[4]) ? jsons[4] : [],
+ };
+
+ if (isMounted) {
+ detailCache.set(
+ detailCacheKey,
+ JSON.stringify({ timestamp: Date.now(), data: combined }),
+ );
+ setData({ ...combined, lastChecked: Date.now() });
+ }
+ } catch (e: any) {
+ if (e.name !== "AbortError" && !e.message?.includes("aborted"))
+ console.error(e);
+ } finally {
+ if (isMounted) setIsLoading(false);
+ }
+ };
+ fetchDetailData();
+ return () => {
+ isMounted = false;
+ abort.abort();
+ };
+ }, [gameId, gameTitle, SHOW_CHART, refreshKey]);
+
+ const {
+ steamData,
+ realBundles,
+ deals,
+ historyLow,
+ overview,
+ historyChart,
+ lastChecked,
+ } = data;
+
+ // โฑ๏ธ TIME DOMAIN: Single deterministic snapshot
+ const now = useMemo(() => Date.now(), [refreshKey]);
+
+ // ๐ฆ BUNDLE DOMAIN: Single source of truth
+ const bundle = useMemo(() => {
+ const isBundleActive = (b: any) => {
+ if (!b?.expiry) return true;
+ const t = new Date(b.expiry).getTime();
+ return Number.isFinite(t) && t > now;
+ };
+
+ const activeBundles = realBundles.filter(isBundleActive);
+ const activeCount = activeBundles.length;
+
+ const recentBundles = realBundles.filter((b: any) => {
+ const tsRaw = b.created ?? b.timestamp;
+ const ts = tsRaw ? new Date(tsRaw).getTime() : null;
+ return ts && ts < now && now - ts < RECENT_BUNDLE_WINDOW;
+ });
+
+ const totalBundles =
+ realBundles?.length > 0
+ ? realBundles.length
+ : typeof overview?.bundles === "number"
+ ? overview.bundles
+ : overview?.bundles?.count || overview?.bundles?.length || 0;
+
+ const allBundlesForTs = Array.isArray(overview?.bundles)
+ ? overview.bundles
+ : realBundles;
+
+ const timestamps = allBundlesForTs
+ .map((b: any) => {
+ const ts = b.created ?? b.timestamp ?? b.publish ?? b.expiry;
+ return ts ? new Date(ts).getTime() : null;
+ })
+ .filter((t: any) => t !== null && !isNaN(t));
+
+ const lastBundleTs =
+ timestamps.length > 0 ? Math.max(...timestamps) : undefined;
+ const lastBundleDate = lastBundleTs ? new Date(lastBundleTs) : null;
+ const lastBundleAgo = lastBundleTs ? now - lastBundleTs : Infinity;
+
+ const ONE_YEAR = 365 * 24 * 60 * 60 * 1000;
+
+ let state: string | null = null;
+ let icon: Image.ImageLike | undefined = undefined;
+ let color: Color | undefined = undefined;
+
+ if (activeCount > 0) {
+ state = "Active";
+ icon = Icon.Box;
+ color = Color.Purple;
+ } else if (totalBundles === 0) {
+ state = "Never Bundled";
+ icon = Icon.XMarkCircle;
+ color = Color.SecondaryText;
+ } else if (recentBundles.length >= 4 && lastBundleAgo < ONE_YEAR) {
+ state = "Frequent";
+ icon = Icon.Repeat;
+ color = Color.Orange;
+ } else if (totalBundles >= 2 && lastBundleAgo < ONE_YEAR) {
+ state = "Occasional";
+ icon = Icon.Circle;
+ color = Color.SecondaryText;
+ } else if (totalBundles === 1 && lastBundleAgo < ONE_YEAR) {
+ state = "Bundled recently";
+ icon = Icon.Circle;
+ color = Color.SecondaryText;
+ } else if (lastBundleDate) {
+ const month = lastBundleDate.toLocaleString("en-US", { month: "short" });
+ const year = lastBundleDate.getFullYear();
+ state = `Last bundled ${month} ${year}`;
+ icon = Icon.Clock;
+ color = Color.SecondaryText;
+ } else if (totalBundles > 0) {
+ state = `Bundled ${totalBundles} times`;
+ icon = Icon.Clock;
+ color = Color.SecondaryText;
+ }
+
+ const getLowestPrice = (bundle: any) => {
+ const prices = bundle.tiers
+ ?.map((t: any) => t.price?.amount)
+ .filter((p: number | undefined) => typeof p === "number");
+ return prices?.length ? Math.min(...prices) : Infinity;
+ };
+
+ const featuredBundle =
+ activeBundles.length > 0
+ ? activeBundles.reduce(
+ (best: any, current: any) =>
+ getLowestPrice(current) < getLowestPrice(best) ? current : best,
+ activeBundles[0],
+ )
+ : null;
+
+ const featuredPrice = featuredBundle
+ ? getLowestPrice(featuredBundle)
+ : null;
+
+ const getGameTierPrice = (b: any) => {
+ const tiersWithGame = b.tiers?.filter((t: any) =>
+ t.games?.some((gm: any) => gm.id === gameId),
+ );
+ if (tiersWithGame && tiersWithGame.length > 0) {
+ const prices = tiersWithGame
+ .map((t: any) => t.price?.amount)
+ .filter((p: any) => typeof p === "number");
+ return prices.length > 0 ? Math.min(...prices) : Infinity;
+ }
+ return Infinity;
+ };
+
+ const bestGameTierPrice =
+ activeBundles.length > 0
+ ? Math.min(...activeBundles.map(getGameTierPrice))
+ : null;
+
+ const actualBundlePrice =
+ bestGameTierPrice !== Infinity && bestGameTierPrice !== null
+ ? bestGameTierPrice
+ : featuredPrice;
+
+ return {
+ activeBundles,
+ activeCount,
+ recentBundles,
+ totalBundles,
+ state,
+ icon,
+ color,
+ featuredBundle,
+ featuredPrice,
+ actualBundlePrice,
+ getLowestPrice,
+ };
+ }, [realBundles, overview, now, gameId]);
+
+ const allowedHistory = useMemo(() => {
+ return (historyChart || []).filter(
+ (pt: any) =>
+ pt.deal?.price?.amount != null &&
+ isStoreAllowed(pt.shop?.name || "", selectedStores),
+ );
+ }, [historyChart, selectedStores]);
+
+ const filteredDeals = deals.filter((d: any) =>
+ isStoreAllowed(d.shop?.name || "", selectedStores),
+ );
+ const currentBest = filteredDeals?.[0];
+ const currentPrice = currentBest?.price?.amount;
+
+ const bundleValue = useMemo(() => {
+ if (!bundle.activeBundles.length || currentPrice == null) return null;
+
+ for (const b of bundle.activeBundles) {
+ let gameTierIndex = -1;
+ b.tiers?.forEach((t: any, i: number) => {
+ if (
+ t.games?.some((gm: any) => gm.id === gameId || gm.name === gameTitle)
+ ) {
+ gameTierIndex = i;
+ }
+ });
+
+ if (gameTierIndex === -1) continue;
+
+ const tierPrice = b.tiers[gameTierIndex]?.price?.amount;
+ if (!tierPrice) continue;
+
+ if (tierPrice < currentPrice) {
+ return {
+ type: "better",
+ message: "Cheaper in active bundle",
+ tier: b.tiers[gameTierIndex],
+ bundle: b,
+ };
+ }
+
+ let totalGames = 0;
+ for (let i = 0; i <= gameTierIndex; i++) {
+ totalGames += b.tiers[i]?.games?.length || 0;
+ }
+
+ const unitPrice = totalGames > 0 ? tierPrice / totalGames : tierPrice;
+ if (unitPrice < currentPrice) {
+ return {
+ type: "value",
+ message: "Better value in bundle",
+ tier: b.tiers[gameTierIndex],
+ bundle: b,
+ };
+ }
+ }
+ return null;
+ }, [bundle.activeBundles, currentPrice, gameId, gameTitle]);
+
+ const allTimeLow = historyLow?.price?.amount ?? historyLow?.amount;
+ const hCurrency =
+ historyLow?.price?.currency ?? historyLow?.currency ?? "USD";
+
+ // ๐งฎ SCORE DOMAIN: Untouched heuristic engine
+ const twelveMonthTime = now - 365 * 24 * 60 * 60 * 1000;
+ const statsPrices = allowedHistory
+ .filter((pt: any) => new Date(pt.timestamp).getTime() >= twelveMonthTime)
+ .map((pt: any) => pt.deal.price.amount);
+
+ let typicalMin: number | null = null;
+ let typicalMax: number | null = null;
+ let median: number | null = null;
+
+ if (statsPrices.length > 0) {
+ const sorted = [...statsPrices].sort((a, b) => a - b);
+ const filtered = sorted.slice(
+ Math.floor(sorted.length * 0.1),
+ Math.floor(sorted.length * 0.9) + 1,
+ );
+ if (filtered.length > 0) {
+ typicalMin = Math.min(...filtered);
+ typicalMax = Math.max(...filtered);
+ const mid = Math.floor(filtered.length / 2);
+ median =
+ filtered.length % 2 !== 0
+ ? filtered[mid]
+ : (filtered[mid - 1] + filtered[mid]) / 2;
+ }
+ }
+
+ const cut = currentBest?.cut || 0;
+ let verdict = "";
+ let reason: string | undefined;
+ let recommendation = "";
+
+ const mapUI = (v: string) => {
+ switch (v) {
+ case "Free":
+ return { badge: "free", color: Color.Blue, icon: Icon.Gift };
+ case "Strong deal":
+ return { badge: "best", color: Color.Green, icon: Icon.Star };
+ case "Good deal":
+ return { badge: "good", color: Color.Green, icon: Icon.ThumbsUp };
+ case "Fair price":
+ return {
+ badge: "neutral",
+ color: Color.SecondaryText,
+ icon: Icon.Minus,
+ };
+ case "Not great":
+ case "Not ideal":
+ return { badge: "weak", color: Color.Orange, icon: Icon.Clock };
+ case "Overpriced":
+ return { badge: "bad", color: Color.Red, icon: Icon.XMarkCircle };
+ default:
+ return {
+ badge: "neutral",
+ color: Color.SecondaryText,
+ icon: Icon.Minus,
+ };
+ }
+ };
+
+ if (currentPrice === 0 || cut === 100) {
+ recommendation = "๐ FREE";
+ verdict = "Free";
+ reason = "Free to claim";
+ } else if (bundle.activeCount > 0 && bundleValue?.type === "better") {
+ recommendation = "๐ด HIGH PRICE";
+ verdict = "Overpriced";
+ reason = bundleValue.message;
+ } else if (bundle.activeCount > 0 && bundleValue?.type === "value") {
+ recommendation = "๐ก FAIR PRICE";
+ verdict = "Not ideal";
+ reason = bundleValue.message;
+ } else if (currentPrice != null) {
+ let score = 0;
+ const safeATL =
+ allTimeLow && allTimeLow > 0 ? allTimeLow : currentPrice || 1;
+ const ratioATL = currentPrice / safeATL;
+
+ if (ratioATL <= 0.95) score += 0.35;
+ else if (ratioATL <= 1.05) score += 0.25;
+ else if (ratioATL <= 1.2) score += 0.1;
+ else if (ratioATL >= 2) score -= 0.2;
+
+ if (median != null && median > 0) {
+ const ratioMedian = currentPrice / median;
+ if (ratioMedian <= 0.75) score += 0.25;
+ else if (ratioMedian <= 0.9) score += 0.15;
+ else if (ratioMedian >= 1.25) score -= 0.2;
+ }
+
+ if (cut >= 75 && ratioATL <= 1.2) score += 0.4;
+ else if (cut >= 75) score += 0.3;
+ else if (cut >= 50) score += 0.2;
+ else if (cut >= 25) score += 0.1;
+ else if (cut > 0) score += 0.05;
+
+ score = Math.max(0, Math.min(1, score));
+
+ // Recommendation thresholds
+ if (score >= 0.7) recommendation = "๐ฅ GREAT DEAL";
+ else if (score >= 0.5) recommendation = "๐ GOOD DEAL";
+ else if (score >= 0.35) recommendation = "๐ก FAIR PRICE";
+ else recommendation = "๐ด HIGH PRICE";
+
+ const isATL = currentPrice <= safeATL;
+ const isNearATL = currentPrice <= safeATL * 1.05;
+ const isBelowAvg = median && currentPrice < median * 0.85;
+ const isAtTypical = median && currentPrice <= median * 1.05;
+
+ if (score < 0.35) {
+ if (cut === 0 && (!median || isAtTypical)) {
+ verdict = "Fair price";
+ reason = "Typical price for this game";
+ recommendation = "๐ก FAIR PRICE";
+ } else {
+ verdict = cut > 0 ? "Not ideal" : "Overpriced";
+ reason =
+ cut > 0 ? "Discounted, but still high" : "Above usual price range";
+ }
+ } else if (score < 0.5) {
+ verdict = "Not ideal";
+ if (bundle.state === "Frequent") reason = "Frequently bundled, wait";
+ else if (cut >= 70) reason = "Big discount, not lowest";
+ else
+ reason = cut > 0 ? "Small discount, better wait" : "No discount, wait";
+ } else {
+ if (isATL) {
+ verdict = score >= 0.7 ? "Strong deal" : "Good deal";
+ reason = cut > 0 ? "At all-time low price" : "Lowest recorded price";
+ } else if (isNearATL) {
+ verdict = "Good deal";
+ reason = "Near all-time low price";
+ } else if (cut >= 75) {
+ verdict = "Good deal";
+ reason = "Large discount applied";
+ } else if (isBelowAvg) {
+ verdict = "Good deal";
+ reason = "Well below usual price";
+ } else {
+ verdict = "Fair price";
+ reason = "Decent price, not the lowest";
+ }
+ }
+ }
+
+ if (!reason) {
+ reason = "Typical pricing";
+ }
+
+ const plotData: any[] = [];
+ const cutoffTime =
+ now -
+ (range === "3m" ? 90 : range === "6m" ? 180 : 365) * 24 * 60 * 60 * 1000;
+ if (allowedHistory.length > 0) {
+ allowedHistory
+ .filter((pt: any) => new Date(pt.timestamp).getTime() >= cutoffTime)
+ .reverse()
+ .forEach((pt: any) => {
+ plotData.push({
+ x: new Date(pt.timestamp).toISOString().split("T")[0],
+ y: pt.deal.price.amount,
+ });
+ });
+ }
+
+ let chartUrl = "";
+ if (SHOW_CHART && plotData.length > 0) {
+ const minY = Math.min(...plotData.map((p) => p.y));
+ const datasets: any[] = [
+ {
+ data: plotData,
+ borderColor: "#2ecc71",
+ backgroundColor: "rgba(46, 204, 113, 0.05)",
+ steppedLine: true,
+ fill: true,
+ pointRadius: plotData.map((p) => (Math.abs(p.y - minY) < 0.01 ? 4 : 0)),
+ pointBackgroundColor: plotData.map((p) =>
+ Math.abs(p.y - minY) < 0.01 ? "#e74c3c" : "transparent",
+ ),
+ pointBorderColor: plotData.map((p) =>
+ Math.abs(p.y - minY) < 0.01 ? "#ffffff" : "transparent",
+ ),
+ pointBorderWidth: 2,
+ borderWidth: 2,
+ },
+ ];
+
+ if (median !== null) {
+ datasets.push({
+ data: plotData.map((p) => ({ x: p.x, y: median })),
+ borderColor: "rgba(255, 255, 255, 0.2)",
+ borderWidth: 1,
+ borderDash: [5, 5],
+ fill: false,
+ pointRadius: 0,
+ });
+ }
+
+ const config: any = {
+ type: "line",
+ data: { datasets },
+ options: {
+ layout: { padding: { right: 30, left: 5, top: 10, bottom: 5 } },
+ legend: { display: false },
+ scales: {
+ xAxes: [
+ {
+ type: "time",
+ time: {
+ parser: "YYYY-MM-DD",
+ unit: "month",
+ displayFormats: { month: "MMM YY" },
+ },
+ gridLines: { color: "rgba(255, 255, 255, 0.1)" },
+ ticks: { maxRotation: 0, maxTicksLimit: 6, fontSize: 8 },
+ },
+ ],
+ yAxes: [
+ {
+ gridLines: { color: "rgba(255, 255, 255, 0.1)" },
+ ticks: { beginAtZero: true, fontSize: 8 },
+ },
+ ],
+ },
+ annotation: {
+ annotations: [
+ {
+ type: "line",
+ mode: "horizontal",
+ scaleID: "y-axis-0",
+ value: minY,
+ borderColor: "rgba(231, 76, 60, 0.8)",
+ borderWidth: 1,
+ borderDash: [2, 2],
+ label: {
+ enabled: true,
+ content: "ATL",
+ position: "right",
+ backgroundColor: "rgba(231, 76, 60, 0.8)",
+ fontSize: 8,
+ yAdjust: 6,
+ },
+ },
+ ],
+ },
+ },
+ };
+ chartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(config))}&w=250&h=110&devicePixelRatio=2&bkg=transparent`;
+ }
+
+ const isDiscounted = currentBest && currentBest.cut > 0;
+ let saleTagText = "";
+ if (isDiscounted) {
+ if (currentBest.cut >= 70) saleTagText = "MEGA SALE";
+ else if (currentBest.cut >= 40) saleTagText = "ON SALE";
+ else saleTagText = "DISCOUNT";
+ }
+
+ const heroSection =
+ currentBest && currentPrice != null
+ ? `${recommendation || "Price Details"}
\n${formatPrice(currentPrice, currentBest.price?.currency)} ${isDiscounted ? `-${currentBest.cut}%` : ""} ยท ${currentBest.shop?.name}
\n\n---\n\n`
+ : "";
+
+ const markdown = `
+${steamData?.header_image ? `
\n\n` : ""}
+# ${gameTitle}
+${
+ steamData?.genres
+ ? `*${steamData.genres
+ .map((g: any) => g.description)
+ .slice(0, 2)
+ .join(
+ ", ",
+ )}*${steamData?.release_date?.date ? ` ยท ${new Date(steamData.release_date.date).getFullYear()}` : ""}`
+ : ""
+}
+
+${steamData?.short_description ? `> ${steamData.short_description.replace(/<[^>]*>?/gm, "").split(". ")[0]}.` : ""}
+
+${heroSection}
+๐ฐ **Prices in ${COUNTRY}**
+
+| Store | Price | RRP | Discount |
+| :--- | :--- | :--- | :--- |
+${filteredDeals?.length ? filteredDeals.map((p: any) => `| ${p.url ? `[${p.shop?.name}](${p.url})` : p.shop?.name} | **${formatPrice(p.price?.amount, p.price?.currency)}** | ${formatPrice(p.regular?.amount, p.price?.currency)} | ${p.cut > 0 ? "-" + p.cut + "%" : "-"} |`).join("\n") : "| No data found | - | - | - |"}
+
+${chartUrl ? `\n---\n\n๐ **Trend: ${range === "1y" ? "12 Months" : range === "6m" ? "6 Months" : "3 Months"}**\n\n\n` : ""}
+`;
+
+ return (
+
+ {recommendation && (
+
+ )}
+ {verdict &&
+ ["๐ฅ GREAT DEAL", "๐ GOOD DEAL", "๐ FREE"].includes(
+ recommendation,
+ ) && (
+
+ )}
+ {reason && (
+ 28 ? reason.slice(0, 25) + "..." : reason}
+ />
+ )}
+ {(isDiscounted || bundle.activeCount > 0) && (
+ <>
+
+
+ {isDiscounted && (
+
+ )}
+ {bundle.activeCount > 0 && (
+
+ )}
+
+ >
+ )}
+
+
+ {typicalMin !== null &&
+ typicalMax !== null &&
+ typicalMin !== typicalMax && (
+
+ )}
+ {median !== null && (
+
+ )}
+
+ {bundle.state && (
+ <>
+
+
+ >
+ )}
+ {bundleValue?.tier && bundleValue?.bundle && (
+
+ )}
+
+
+ = 23
+ ? "All Stores"
+ : `${selectedStores.length} Selected`
+ }
+ />
+
+
+
+
+ {gameSlug && (
+
+ )}
+ {steamData?.steam_appid && (
+
+ )}
+ {lastChecked && (
+ <>
+
+
+ >
+ )}
+
+ }
+ actions={
+
+
+ {currentBest?.url && (
+
+ )}
+
+ {currentBest?.url && (
+
+ )}
+
+ handleSetRange("3m")}
+ icon={range === "3m" ? Icon.Checkmark : Icon.Circle}
+ />
+ handleSetRange("6m")}
+ icon={range === "6m" ? Icon.Checkmark : Icon.Circle}
+ />
+ handleSetRange("1y")}
+ icon={range === "1y" ? Icon.Checkmark : Icon.Circle}
+ />
+
+ {
+ const c = new Cache({ namespace: "search_detail" });
+ c.remove(`search_detail_${gameId}_${COUNTRY}_v1`);
+ setIsLoading(true);
+ setRefreshKey((k) => k + 1);
+ }}
+ />
+ {realBundles.length > 0 && (
+
+ }
+ icon={Icon.Box}
+ shortcut={{
+ Windows: { modifiers: ["ctrl"], key: "b" },
+ macOS: { modifiers: ["cmd"], key: "b" },
+ }}
+ />
+ )}
+ {toggleSave && (
+
+ )}
+ {removeGame && (
+
+ )}
+
+
+ }
+ />
+ );
+}
+
+function BundleContentViewer({ bundles, gameTitle }: any) {
+ const firstBundleUrl = cleanBundleUrl(
+ bundles?.[0]?.url || bundles?.[0]?.details,
+ );
+
+ let markdown = `# ๐ฆ Bundle Contents for ${gameTitle}\n\n`;
+ bundles.forEach((b: any, i: number) => {
+ const active = b.expiry ? new Date(b.expiry) > new Date() : true;
+ markdown += `## ${active ? "โ
" : "โ"} ${b.title || `Bundle ${i + 1}`}\n**Page:** ${b.page?.name || "Unknown"}${b.expiry ? ` | **Expires:** ${new Date(b.expiry).toLocaleDateString("en-GB")}` : ""}\n${b.note ? `\n> ${b.note}` : ""}\n\n`;
+ b.tiers?.forEach((t: any, ti: number) => {
+ markdown += `### ${t.name || `Tier ${ti + 1}`} - **${t.price ? formatPrice(t.price.amount, t.price.currency) : "N/A"}**\n`;
+ t.games?.forEach(
+ (g: any) => (markdown += `- ${g.title || g.name || g}\n`),
+ );
+ markdown += `\n`;
+ });
+ });
+ return (
+
+
+
+
+ ) : undefined
+ }
+ />
+ );
+}
diff --git a/extensions/game-scout/src/top-deals.tsx b/extensions/game-scout/src/top-deals.tsx
new file mode 100644
index 00000000000..e0f1428fae8
--- /dev/null
+++ b/extensions/game-scout/src/top-deals.tsx
@@ -0,0 +1,218 @@
+import { useEffect, useState } from "react";
+import {
+ List,
+ ActionPanel,
+ Action,
+ Icon,
+ Color,
+ Cache,
+ getPreferenceValues,
+ LocalStorage,
+} from "@raycast/api";
+
+const STORES: { [key: string]: { name: string; color: Color } } = {
+ "1": { name: "Steam", color: Color.Blue },
+ "2": { name: "GamersGate", color: Color.SecondaryText },
+ "3": { name: "Green Man Gaming", color: Color.Green },
+ "4": { name: "Amazon", color: Color.Orange },
+ "5": { name: "GameStop", color: Color.Red },
+ "6": { name: "Direct2Drive", color: Color.SecondaryText },
+ "7": { name: "GOG", color: Color.Purple },
+ "8": { name: "Origin", color: Color.Orange },
+ "9": { name: "Get Games", color: Color.SecondaryText },
+ "10": { name: "ShinyLoot", color: Color.SecondaryText },
+ "11": { name: "Humble Store", color: Color.Red },
+ "12": { name: "Desura", color: Color.SecondaryText },
+ "13": { name: "Uplay", color: Color.Blue },
+ "14": { name: "IndieGameStand", color: Color.SecondaryText },
+ "15": { name: "Fanatical", color: Color.Orange },
+ "16": { name: "Gamesrocket", color: Color.SecondaryText },
+ "17": { name: "Games Republic", color: Color.SecondaryText },
+ "18": { name: "Sila Games", color: Color.SecondaryText },
+ "19": { name: "Playfield", color: Color.SecondaryText },
+ "20": { name: "ImperialGames", color: Color.SecondaryText },
+ "21": { name: "WinGameStore", color: Color.SecondaryText },
+ "22": { name: "FunStockDigital", color: Color.SecondaryText },
+ "23": { name: "GameBillet", color: Color.SecondaryText },
+ "24": { name: "Voidu", color: Color.SecondaryText },
+ "25": { name: "Epic Games Store", color: Color.SecondaryText },
+ "26": { name: "Razer Game Store", color: Color.SecondaryText },
+ "27": { name: "Gamesplanet", color: Color.SecondaryText },
+ "28": { name: "Gamesload", color: Color.SecondaryText },
+ "29": { name: "2Game", color: Color.SecondaryText },
+ "30": { name: "IndieGala", color: Color.SecondaryText },
+ "31": { name: "Blizzard Shop", color: Color.SecondaryText },
+ "32": { name: "AllYouPlay", color: Color.SecondaryText },
+ "33": { name: "DLGamer", color: Color.SecondaryText },
+ "34": { name: "Noctre", color: Color.SecondaryText },
+ "35": { name: "DreamGame", color: Color.SecondaryText },
+};
+
+const CHEAPSHARK_MAP: Record = {
+ "1": "steam",
+ "2": "gamersgate",
+ "3": "gmg",
+ "7": "gog",
+ "8": "ea",
+ "11": "humble",
+ "13": "ubisoft",
+ "15": "fanatical",
+ "21": "wingamestore",
+ "23": "gamebillet",
+ "24": "voidu",
+ "25": "epic",
+ "27": "gamesplanet",
+ "29": "2game",
+ "30": "indiegala",
+ "31": "blizzard",
+ "32": "allyouplay",
+ "33": "dlgamer",
+};
+
+const cache = new Cache();
+export default function TopDeals() {
+ const [deals, setDeals] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedStores, setSelectedStores] = useState(null);
+
+ const preferences = getPreferenceValues();
+ const minDiscount = preferences.minDiscount || "0";
+ const maxPrice = preferences.maxPrice || "50";
+
+ useEffect(() => {
+ LocalStorage.getItem("selected_stores").then((stored) => {
+ if (stored) setSelectedStores(JSON.parse(stored));
+ else setSelectedStores(["all"]);
+ });
+ }, []);
+
+ useEffect(() => {
+ const fetchDeals = async () => {
+ setIsLoading(true);
+ const cacheKey = `top_deals_${minDiscount}_${maxPrice}`;
+ const cachedData = cache.get(cacheKey);
+
+ if (cachedData) {
+ const parsedData = JSON.parse(cachedData);
+ const cacheAge = Date.now() - parsedData.timestamp;
+ if (cacheAge < 60 * 60 * 1000) {
+ setDeals(parsedData.deals);
+ setIsLoading(false);
+ return;
+ }
+ }
+
+ try {
+ const response = await fetch(
+ `https://www.cheapshark.com/api/1.0/deals?upperPrice=${maxPrice}&onSale=1&sortBy=Deal%20Rating`,
+ );
+ const data = await response.json();
+ data.sort((a: any, b: any) => {
+ const ratingDiff =
+ parseFloat(b.dealRating) - parseFloat(a.dealRating);
+ if (ratingDiff !== 0) return ratingDiff;
+ const savingsDiff = parseFloat(b.savings) - parseFloat(a.savings);
+ if (savingsDiff !== 0) return savingsDiff;
+ return parseFloat(b.salePrice) - parseFloat(a.salePrice);
+ });
+ const filteredByDiscount = data.filter(
+ (deal: any) => parseFloat(deal.savings) >= parseFloat(minDiscount),
+ );
+
+ cache.set(
+ cacheKey,
+ JSON.stringify({ timestamp: Date.now(), deals: filteredByDiscount }),
+ );
+ setDeals(filteredByDiscount);
+ } catch (error) {
+ console.error("Error fetching deals:", error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchDeals();
+ }, [minDiscount, maxPrice]);
+
+ const filteredDeals = deals.filter((deal: any) => {
+ if (selectedStores === null) return false;
+ if (selectedStores.includes("all")) return true;
+ const mappedId = CHEAPSHARK_MAP[deal.storeID] || "other";
+ return selectedStores.includes(mappedId);
+ });
+
+ return (
+
+ {filteredDeals.map((deal) => {
+ const store = STORES[deal.storeID] || {
+ name: "Unknown",
+ color: Color.SecondaryText,
+ };
+ const discount = Math.round(parseFloat(deal.savings));
+
+ return (
+
+
+
+
+ {deal.metacriticLink && (
+ <>
+
+
+ >
+ )}
+
+ }
+ />
+ );
+ })}
+
+ );
+}
diff --git a/extensions/game-scout/src/utils.ts b/extensions/game-scout/src/utils.ts
new file mode 100644
index 00000000000..ed6a8ec2cd4
--- /dev/null
+++ b/extensions/game-scout/src/utils.ts
@@ -0,0 +1,111 @@
+export const STORE_MAP: Record = {
+ steam: ["Steam"],
+ epic: ["Epic Games Store"],
+ gog: ["GOG"],
+ humble: ["Humble Store", "Humble Widget"],
+ fanatical: ["Fanatical"],
+ gmg: ["Green Man Gaming"],
+ ea: ["Origin", "EA App", "EA Store"],
+ ubisoft: ["Ubisoft Store", "Uplay"],
+ blizzard: ["Blizzard Shop", "Battle.net"],
+ microsoft: ["Microsoft Store", "Xbox Store"],
+ gamersgate: ["GamersGate"],
+ indiegala: ["IndieGala Store", "IndieGala"],
+ dlgamer: ["DLGamer"],
+ gamebillet: ["GameBillet"],
+ voidu: ["Voidu"],
+ gamesplanet: [
+ "GamesPlanet US",
+ "GamesPlanet UK",
+ "GamesPlanet FR",
+ "GamesPlanet DE",
+ ],
+ wingamestore: ["WinGameStore", "MacGameStore"],
+ "2game": ["2Game"],
+ allyouplay: ["AllYouPlay"],
+ etailmarket: ["eTail.Market"],
+ joybuggy: ["JoyBuggy"],
+ planetplay: ["PlanetPlay"],
+ other: [],
+};
+
+export const STORE_LOOKUP: Record = {};
+for (const [id, names] of Object.entries(STORE_MAP)) {
+ names.forEach((name) => {
+ STORE_LOOKUP[name] = id;
+ });
+}
+
+export function formatPrice(
+ amount: number | undefined,
+ currency: string | undefined,
+): string {
+ if (amount === undefined || amount === null) return "-";
+ if (amount === 0) return "FREE";
+ const symbols: Record = {
+ USD: "$",
+ GBP: "ยฃ",
+ EUR: "โฌ",
+ TRY: "โบ",
+ JPY: "ยฅ",
+ CNY: "ยฅ",
+ KRW: "โฉ",
+ INR: "โน",
+ RUB: "โฝ",
+ BRL: "R$",
+ CAD: "CA$",
+ AUD: "A$",
+ NZD: "NZ$",
+ HKD: "HK$",
+ SGD: "S$",
+ MXN: "MX$",
+ NOK: "kr",
+ SEK: "kr",
+ DKK: "kr",
+ PLN: "zล",
+ CZK: "Kฤ",
+ HUF: "Ft",
+ CHF: "CHF",
+ ZAR: "R",
+ SAR: "๏ทผ",
+ AED: "ุฏ.ุฅ",
+ QAR: "๏ทผ",
+ THB: "เธฟ",
+ IDR: "Rp",
+ MYR: "RM",
+ PHP: "โฑ",
+ TWD: "NT$",
+ ARS: "ARS$",
+ CLP: "CLP$",
+ COP: "COP$",
+ UYU: "UYU$",
+ KZT: "โธ",
+ ILS: "โช",
+ UAH: "โด",
+ };
+ const curr = currency || "USD";
+ const symbol = symbols[curr] || curr + " ";
+ const noDecimals = ["JPY", "KRW"];
+ return `${symbol}${noDecimals.includes(curr) ? Math.round(amount) : amount.toFixed(2)}`;
+}
+
+export function isStoreAllowed(
+ shopName: string,
+ selectedStores: string[],
+): boolean {
+ if (
+ selectedStores.length === 0 ||
+ (selectedStores.length === 1 && selectedStores[0] === "all")
+ )
+ return true;
+ return selectedStores.includes(STORE_LOOKUP[shopName] || "other");
+}
+
+export function getTimeContext(timestamp: number): string {
+ const days = Math.floor((Date.now() - timestamp) / (1000 * 60 * 60 * 24));
+ if (days === 0) return "today";
+ if (days < 30) return `${days} days ago`;
+ const months = Math.floor(days / 30);
+ if (months < 12) return `${months} months ago`;
+ return `${(days / 365).toFixed(1)} years ago`;
+}
diff --git a/extensions/game-scout/tsconfig.json b/extensions/game-scout/tsconfig.json
new file mode 100644
index 00000000000..fe886e6dfaa
--- /dev/null
+++ b/extensions/game-scout/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "target": "es2022",
+ "lib": ["es2022", "dom"],
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "isolatedModules": true
+ },
+ "include": ["src"]
+}
\ No newline at end of file