diff --git a/extensions/is-it-alive/.gitignore b/extensions/is-it-alive/.gitignore new file mode 100644 index 00000000000..56c52f42f56 --- /dev/null +++ b/extensions/is-it-alive/.gitignore @@ -0,0 +1,16 @@ +# 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 +compiled_raycast_rust + +# misc +.DS_Store + +.pnpm-store diff --git a/extensions/is-it-alive/CHANGELOG.md b/extensions/is-it-alive/CHANGELOG.md new file mode 100644 index 00000000000..82d48c50314 --- /dev/null +++ b/extensions/is-it-alive/CHANGELOG.md @@ -0,0 +1,10 @@ +# Is It Alive? Changelog + +## [Initial Release] - {PR_MERGE_DATE} + +- Monitor status pages from a single Raycast command +- Auto-detect providers: Statuspage.io, Better Stack, incident.io, and Railway +- Add, edit, and delete monitored sites with local storage persistence +- Color-coded site list with parallel fetch and incident accessories +- Detail view with overview, active incidents, and per-component status +- 90-day uptime history charts with SVG bar visualization diff --git a/extensions/is-it-alive/LICENSE b/extensions/is-it-alive/LICENSE new file mode 100644 index 00000000000..e5a55d2a0d7 --- /dev/null +++ b/extensions/is-it-alive/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Alerix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extensions/is-it-alive/README.md b/extensions/is-it-alive/README.md new file mode 100644 index 00000000000..99da9515aeb --- /dev/null +++ b/extensions/is-it-alive/README.md @@ -0,0 +1,106 @@ +

+ Is It Alive? icon +

+ +

Is It Alive?

+ +

+ Raycast extension that monitors status pages so you can quickly answer: is the outage on my side or theirs? +

+ +

+ Add status page URLs, get a color-coded list of services, and drill into component-level detail with uptime history — without opening a browser tab. +

+ +## Screenshots + +

+ Monitored sites list with live status +

+ +

+ Site list — parallel fetch, status subtitles, and quick access to details. +

+ +

+ Status detail view with 90-day uptime charts +

+ +

+ Detail view — component breakdown, active incidents, and 90-day uptime history. +

+ +## Supported status pages + +The extension auto-detects the provider when you add a URL. Detection order: **Railway → incident.io → Better Stack → Statuspage**. + +| Provider | Examples | Detection | +| --- | --- | --- | +| **Railway** | [status.railway.com](https://status.railway.com) | Hostname match | +| **incident.io** | [status.openai.com](https://status.openai.com) | `/proxy/{host}/component_impacts` API | +| **Better Stack** | [status.yachtway.com](https://status.yachtway.com) | `/index.json` JSON:API | +| **Statuspage.io** | [status.claude.com](https://status.claude.com), GitHub, Vercel | `/api/v2/summary.json` | + +incident.io is checked before Statuspage because some Statuspage hosts expose proxy-style URLs that look similar but lack incident.io-only endpoints like `component_impacts`. + +## Features + +- **Site list** — parallel fetch, status subtitle, incident accessories +- **Add / edit / delete** — sites stored in Raycast local storage +- **Detail view** — overview, active incidents, per-component status +- **Uptime charts** — 90-day SVG bar history with uptime percentage + + +## Usage + +1. Open **Is It Alive?** in Raycast +2. Add a status page URL (display name is optional — it defaults to the page title) +3. Press Enter to preview + +## Development + +Requires [Raycast](https://raycast.com), Node.js, and npm. + +```bash +npm install +npm run dev # run in Raycast dev mode +npm run lint # lint + format check +npm run build # production build +``` + +### Project structure + +``` +src/ + alive.tsx # main list command + adapters/ + index.ts # provider detection + registry + statuspage.ts # Statuspage.io v2 API + betterstack.ts # Better Stack /index.json API + incident-io.ts # incident.io proxy API + railway.ts # Railway status API + components/ + site-form.tsx # add / edit form + site-detail.tsx # preview with uptime charts + hooks/use-sites.ts # local storage CRUD + lib/ + fetch-json.ts # shared JSON fetch helper + snapshot-text.ts # uptime labels + status descriptions + status-colors.ts # indicator + component colors + uptime-chart.ts # SVG chart generation + uptime math + url.ts # URL normalization + Railway host check + types/ # shared + provider-specific API types +``` + +### Adding a new provider + +1. Add a `SiteProvider` variant in `src/types/index.ts` +2. Add provider API types under `src/types/` if needed +3. Implement `StatusAdapter` (`detect?` + `fetchSnapshot`) in `src/adapters/` +4. Register it in `src/adapters/index.ts` and update `detectProvider` + +Each adapter normalizes its API into a shared `StatusSnapshot` shape so the UI stays provider-agnostic. + +## License + +MIT diff --git a/extensions/is-it-alive/assets/extension-icon.png b/extensions/is-it-alive/assets/extension-icon.png new file mode 100644 index 00000000000..1cb7c681544 Binary files /dev/null and b/extensions/is-it-alive/assets/extension-icon.png differ diff --git a/extensions/is-it-alive/eslint.config.js b/extensions/is-it-alive/eslint.config.js new file mode 100644 index 00000000000..543274e97ec --- /dev/null +++ b/extensions/is-it-alive/eslint.config.js @@ -0,0 +1,4 @@ +const { defineConfig } = require("eslint/config"); +const raycastConfig = require("@raycast/eslint-config"); + +module.exports = defineConfig([...raycastConfig]); diff --git a/extensions/is-it-alive/metadata/is-it-alive-1.png b/extensions/is-it-alive/metadata/is-it-alive-1.png new file mode 100644 index 00000000000..f350261cede Binary files /dev/null and b/extensions/is-it-alive/metadata/is-it-alive-1.png differ diff --git a/extensions/is-it-alive/metadata/is-it-alive-2.png b/extensions/is-it-alive/metadata/is-it-alive-2.png new file mode 100644 index 00000000000..9bbe89feaf8 Binary files /dev/null and b/extensions/is-it-alive/metadata/is-it-alive-2.png differ diff --git a/extensions/is-it-alive/package-lock.json b/extensions/is-it-alive/package-lock.json new file mode 100644 index 00000000000..e0309ff4912 --- /dev/null +++ b/extensions/is-it-alive/package-lock.json @@ -0,0 +1,2996 @@ +{ + "name": "is-it-alive", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "is-it-alive", + "license": "MIT", + "dependencies": { + "@raycast/api": "^1.103.0", + "@raycast/utils": "^2.2.1" + }, + "devDependencies": { + "@raycast/eslint-config": "^2.0.4", + "@types/node": "22.19.17", + "@types/react": "19.0.10", + "eslint": "^9.22.0", + "prettier": "^3.5.3", + "typescript": "^5.8.2" + } + }, + "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/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/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/@eslint/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/@eslint/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/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.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/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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/@oclif/core": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.11.3.tgz", + "integrity": "sha512-gQCSYAtUhJilGKaSaZhqejH9X1dDu+jWQjLmtGOgN/XcKaAEPPSeT2mu1UvlvtPox1/NNRdlBcUa8KRKo2HnJQ==", + "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.8.0", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.16", + "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.49", + "resolved": "https://registry.npmjs.org/@oclif/plugin-autocomplete/-/plugin-autocomplete-3.2.49.tgz", + "integrity": "sha512-+rrAZ468bW/B9uVrn6sEnFYepy3M1N/BWht8mHzhFIFCIduPSoE+8MweROxZLOGBZrXGWt0iavuPQmy0eaXRfQ==", + "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.49", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.49.tgz", + "integrity": "sha512-fEsO0YU7ThtzHE1RGuoHxFu/OGlqxm7PCfFp+U1PS8sde4E0cDqjVDuv78+VKrr45LpC5lWOApj7pm3FNfHrVA==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-not-found": { + "version": "3.2.86", + "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.86.tgz", + "integrity": "sha512-BJhJSahwsYayZpo18f0fPTg8tKb9dIvydaz03NCK3eMfmcsT1MmXhXqh1KEV8J7mz0sQ6f0qFEb6BXy490/iUg==", + "license": "MIT", + "dependencies": { + "@inquirer/prompts": "^7.10.1", + "@oclif/core": "^4.11.3", + "ansis": "^3.17.0", + "fast-levenshtein": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@raycast/api": { + "version": "1.104.18", + "resolved": "https://registry.npmjs.org/@raycast/api/-/api-1.104.18.tgz", + "integrity": "sha512-Effu0wSTRwtW7VNjyrDtFNxMQ4BbtRFCIY00PbY4qa3yFPOE/CfbcZJbesyo3nYyuenedqeia/+PvLcieBUO4w==", + "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.19.17", + "@types/react": "19.0.10", + "esbuild": "^0.27.3", + "react": "19.0.0" + }, + "bin": { + "ray": "bin/run.js" + }, + "engines": { + "node": ">=22.22.2" + }, + "peerDependencies": { + "@types/node": "22.19.17", + "@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/eslint-config": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@raycast/eslint-config/-/eslint-config-2.1.1.tgz", + "integrity": "sha512-W0kxF+FJ+BYQn0EKIV739j2ZrHEtjo/LclsoZgUWg3t364Dq75XKcjqYFYx+59/DBaamY0amdajlfuDAf6veAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint/js": "^9.36.0", + "@raycast/eslint-plugin": "^2.1.1", + "eslint-config-prettier": "^10.1.8", + "globals": "^16.4.0", + "typescript-eslint": "^8.45.0" + }, + "peerDependencies": { + "eslint": ">=8.23.0", + "prettier": ">=2", + "typescript": ">=4" + } + }, + "node_modules/@raycast/eslint-plugin": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@raycast/eslint-plugin/-/eslint-plugin-2.1.1.tgz", + "integrity": "sha512-r2gs8uIlNp6I2mLOyN/kReGlvigzEeuyQPl4yw7nwLy8Zxjfjhg8txMViaBux8juBWBxbSWq/IfW6ZA50oeOHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.26.1" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/@raycast/utils": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@raycast/utils/-/utils-2.2.5.tgz", + "integrity": "sha512-1o6zGRECh1KNe6CBx68iMPOrNYbV56opqs4zUtDr6Wmq4F7Ww3d8uLTXKVzXlGyJmpAys/Eliw6cKIP7dPdFfw==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "@raycast/api": ">=1.99.4", + "react": ">=19.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "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": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "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/@typescript-eslint/eslint-plugin": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.4", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "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/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.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "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/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": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.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/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.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-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/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": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.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/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": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "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/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-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/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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-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/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/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-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/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/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/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.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "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/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/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/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "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": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", + "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/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/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/is-it-alive/package.json b/extensions/is-it-alive/package.json new file mode 100644 index 00000000000..df632351e50 --- /dev/null +++ b/extensions/is-it-alive/package.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://www.raycast.com/schemas/extension.json", + "name": "is-it-alive", + "description": "Monitor status pages and check if your favorite services are operational.", + "license": "MIT", + "scripts": { + "dev": "ray develop", + "lint": "ray lint", + "fix-lint": "ray lint --fix", + "build": "ray build", + "publish": "npx @raycast/api@latest publish", + "prepublishOnly": "echo \"\\n\\nIt seems like you are trying to publish the Raycast extension to npm.\\n\\nIf you did intend to publish it to npm, remove the \\`prepublishOnly\\` script and rerun \\`npm publish\\` again.\\nIf you wanted to publish it to the Raycast Store instead, use \\`npm run publish\\` instead.\\n\\n\" && exit 1" + }, + "dependencies": { + "@raycast/api": "^1.103.0", + "@raycast/utils": "^2.2.1" + }, + "devDependencies": { + "@raycast/eslint-config": "^2.0.4", + "@types/node": "22.19.17", + "@types/react": "19.0.10", + "eslint": "^9.22.0", + "prettier": "^3.5.3", + "typescript": "^5.8.2" + }, + "icon": "extension-icon.png", + "commands": [ + { + "mode": "view", + "name": "check-status-pages", + "title": "Check Status Pages", + "description": "View live status, active incidents, and uptime history for monitored sites" + } + ], + "title": "Is It Alive?", + "author": "alerix", + "platforms": [ + "macOS" + ], + "categories": [ + "Developer Tools", + "Data" + ] +} diff --git a/extensions/is-it-alive/src/adapters/betterstack.ts b/extensions/is-it-alive/src/adapters/betterstack.ts new file mode 100644 index 00000000000..8375b995298 --- /dev/null +++ b/extensions/is-it-alive/src/adapters/betterstack.ts @@ -0,0 +1,289 @@ +import type { + ComponentStatusValue, + DayStatus, + StatusAdapter, + StatusIncident, + StatusIndicator, + StatusSnapshot, +} from "@/types"; +import { getOrigin, normalizeSiteUrl } from "@/lib/url"; +import { fetchJson } from "@/lib/fetch-json"; +import { overallDescription } from "@/lib/snapshot-text"; +import { + averageComponentUptime, + buildPageHistoryFromComponents, +} from "@/lib/uptime-chart"; +import { + BetterStackAggregateState, + BetterStackIncludedItem, + BetterStackIndexResponse, + BetterStackResourceStatus, + BetterStackStatusHistoryDay, +} from "@/types/betterstack"; + +function indexJsonUrl(siteUrl: string): string { + const origin = getOrigin(normalizeSiteUrl(siteUrl)); + return `${origin}/index.json`; +} + +function isBetterStackResponse( + data: unknown, +): data is BetterStackIndexResponse { + return ( + typeof data === "object" && + data !== null && + "data" in data && + typeof data.data === "object" && + data.data !== null && + "type" in data.data && + data.data.type === "status_page" + ); +} + +function includedByType( + included: BetterStackIncludedItem[], + type: T, +): Extract[] { + return included.filter( + (item): item is Extract => + item.type === type, + ); +} + +function includedById( + included: BetterStackIncludedItem[], + id: string, +): BetterStackIncludedItem | undefined { + return included.find((item) => item.id === id); +} + +function resourceStatusToDayLevel( + status: BetterStackResourceStatus, +): DayStatus["level"] { + switch (status) { + case "operational": + return "operational"; + case "degraded": + return "degraded"; + case "downtime": + return "major"; + case "maintenance": + return "degraded"; + case "not_monitored": + return "unknown"; + default: + return "unknown"; + } +} + +function resourceStatusToComponentStatus( + status: BetterStackResourceStatus, +): ComponentStatusValue | string { + switch (status) { + case "operational": + return "operational"; + case "degraded": + return "degraded_performance"; + case "downtime": + return "major_outage"; + case "maintenance": + return "under_maintenance"; + case "not_monitored": + return "operational"; + default: + return "operational"; + } +} + +function aggregateStateToIndicator( + state: BetterStackAggregateState, +): StatusIndicator { + switch (state) { + case "operational": + case "resolved": + return "none"; + case "degraded": + case "maintenance": + return "minor"; + case "downtime": + return "critical"; + default: + return "minor"; + } +} + +function aggregateStateToImpact(state: BetterStackAggregateState): string { + switch (state) { + case "operational": + case "resolved": + return "none"; + case "degraded": + return "minor"; + case "maintenance": + return "minor"; + case "downtime": + return "critical"; + default: + return "minor"; + } +} + +function aggregateStateDescription(state: BetterStackAggregateState): string { + switch (state) { + case "operational": + return "All Systems Operational"; + case "degraded": + return "Degraded Performance"; + case "downtime": + return "Major Outage"; + case "maintenance": + return "Scheduled Maintenance"; + case "resolved": + return "All Systems Operational"; + default: + return "Unknown"; + } +} + +function buildHistoryDays(history: BetterStackStatusHistoryDay[]): DayStatus[] { + return history.map((day) => ({ + date: day.day, + level: resourceStatusToDayLevel(day.status), + })); +} + +function isActiveReport( + report: Extract, +): boolean { + if (report.attributes.aggregate_state === "resolved") { + return false; + } + + if (report.attributes.ends_at) { + return false; + } + + return report.attributes.affected_resources.some( + (resource) => resource.status !== "resolved", + ); +} + +function mapActiveIncidents( + included: BetterStackIncludedItem[], +): StatusIncident[] { + const reports = includedByType(included, "status_report").filter( + isActiveReport, + ); + + return reports.map((report) => { + const updateIds = + report.relationships.status_updates.data?.map((update) => update.id) ?? + []; + const updates = updateIds + .map((id) => includedById(included, id)) + .filter( + ( + item, + ): item is Extract< + BetterStackIncludedItem, + { type: "status_update" } + > => item?.type === "status_update", + ) + .sort( + (a, b) => + new Date(b.attributes.published_at).getTime() - + new Date(a.attributes.published_at).getTime(), + ); + + const latestUpdate = updates[0]; + + return { + id: report.id, + name: report.attributes.title, + status: report.attributes.aggregate_state, + impact: aggregateStateToImpact(report.attributes.aggregate_state), + updatedAt: + latestUpdate?.attributes.published_at ?? report.attributes.starts_at, + body: latestUpdate?.attributes.message, + affectedComponentIds: report.attributes.affected_resources + .filter((resource) => resource.status !== "resolved") + .map((resource) => resource.status_page_resource_id), + }; + }); +} + +export const betterstackAdapter: StatusAdapter = { + async detect(siteUrl: string): Promise { + try { + const data = await fetchJson(indexJsonUrl(siteUrl)); + return isBetterStackResponse(data); + } catch { + return false; + } + }, + + async fetchSnapshot(siteUrl: string): Promise { + const normalized = normalizeSiteUrl(siteUrl); + const fetchedAt = new Date().toISOString(); + + try { + const response = await fetchJson( + indexJsonUrl(normalized), + ); + + const { attributes } = response.data; + const included = response.included ?? []; + const resources = includedByType(included, "status_page_resource"); + + const components = resources + .map((resource) => ({ + id: resource.id, + name: resource.attributes.public_name, + status: resourceStatusToComponentStatus(resource.attributes.status), + uptimePercent: resource.attributes.availability * 100, + historyDays: buildHistoryDays(resource.attributes.status_history), + })) + .sort((a, b) => a.name.localeCompare(b.name)); + + const incidents = mapActiveIncidents(included); + const indicator = + incidents.length > 0 + ? aggregateStateToIndicator( + incidents.some((incident) => incident.impact === "critical") + ? "downtime" + : "degraded", + ) + : aggregateStateToIndicator(attributes.aggregate_state); + + const overall = + incidents.length > 0 + ? overallDescription(indicator, incidents.length) + : aggregateStateDescription(attributes.aggregate_state); + + const uptimePercent = averageComponentUptime(components); + + return { + pageName: attributes.company_name, + pageUrl: normalized, + overallDescription: overall, + indicator, + components, + incidents, + historyDays: buildPageHistoryFromComponents(components), + uptimePercent, + fetchedAt, + }; + } catch (error) { + return { + pageName: new URL(normalized).hostname, + pageUrl: normalized, + overallDescription: "Failed to fetch", + indicator: "none", + components: [], + incidents: [], + fetchedAt, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, +}; diff --git a/extensions/is-it-alive/src/adapters/incident-io.ts b/extensions/is-it-alive/src/adapters/incident-io.ts new file mode 100644 index 00000000000..140fb1a85f9 --- /dev/null +++ b/extensions/is-it-alive/src/adapters/incident-io.ts @@ -0,0 +1,375 @@ +import type { + ComponentStatusValue, + DayStatus, + StatusAdapter, + StatusIncident, + StatusIndicator, + StatusSnapshot, +} from "@/types"; +import { getOrigin, normalizeSiteUrl } from "@/lib/url"; +import { fetchJson } from "@/lib/fetch-json"; +import { overallDescription } from "@/lib/snapshot-text"; +import { + averageComponentUptime, + buildPageHistoryFromComponents, +} from "@/lib/uptime-chart"; +import { + IncidentIoImpactStatus, + IncidentIoComponentImpact, + IncidentIoComponentUptime, + IncidentIoIncident, + ComponentImpactsResponse, +} from "@/types/incident-io"; +import { STATUS_SEVERITY, DAY_LEVEL_SEVERITY } from "@/types/incident-io"; + +function proxyBase(siteUrl: string): string { + const normalized = normalizeSiteUrl(siteUrl); + const origin = getOrigin(normalized); + const hostname = new URL(normalized).hostname; + return `${origin}/proxy/${hostname}`; +} + +function impactToDayLevel(status: IncidentIoImpactStatus): DayStatus["level"] { + switch (status) { + case "operational": + return "operational"; + case "degraded_performance": + return "degraded"; + case "partial_outage": + return "partial"; + case "full_outage": + return "major"; + default: + return "degraded"; + } +} + +function normalizeComponentStatus( + status: IncidentIoImpactStatus | string, +): ComponentStatusValue | string { + if (status === "full_outage") { + return "major_outage"; + } + + return status; +} + +function statusToIndicator( + status: IncidentIoImpactStatus | string, +): StatusIndicator { + switch (status) { + case "operational": + return "none"; + case "degraded_performance": + return "minor"; + case "partial_outage": + return "major"; + case "full_outage": + return "critical"; + default: + return "minor"; + } +} + +function impactToIncidentImpact(status: IncidentIoImpactStatus): string { + switch (status) { + case "operational": + return "none"; + case "degraded_performance": + return "minor"; + case "partial_outage": + return "major"; + case "full_outage": + return "critical"; + default: + return "minor"; + } +} + +function worstStatus( + statuses: Array, +): IncidentIoImpactStatus { + let worst: IncidentIoImpactStatus = "operational"; + + for (const status of statuses) { + const normalized = status as IncidentIoImpactStatus; + if (STATUS_SEVERITY[normalized] > STATUS_SEVERITY[worst]) { + worst = normalized; + } + } + + return worst; +} + +const ACTIVE_INCIDENT_STATUSES = new Set([ + "investigating", + "identified", + "monitoring", + "in_progress", + "update", +]); + +function isActiveIncident(status: string): boolean { + return ACTIVE_INCIDENT_STATUSES.has(status); +} + +function componentImpactsUrl(proxy: string, days = 90): string { + const end = new Date(); + end.setHours(23, 59, 59, 999); + + const start = new Date(end); + start.setDate(start.getDate() - days); + start.setHours(0, 0, 0, 0); + + const params = new URLSearchParams({ + start_at: start.toISOString(), + end_at: end.toISOString(), + }); + + return `${proxy}/component_impacts?${params.toString()}`; +} + +function parseComponentCatalog(html: string): Map { + const catalog = new Map(); + const pattern = + /\\"id\\":\\"([A-Za-z0-9_-]+)\\",\\"name\\":\\"([^\\"]+)\\",\\"status_page_id\\"/g; + + for (const match of html.matchAll(pattern)) { + catalog.set(match[1], match[2]); + } + + return catalog; +} + +function parsePageTitle(html: string, fallback: string): string { + const match = html.match(/([^<]+)<\/title>/i); + if (!match) { + return fallback; + } + + return match[1].replace(/\s*Status\s*$/i, "").trim() || fallback; +} + +async function fetchPageMetadata( + siteUrl: string, + fallbackName: string, +): Promise<{ catalog: Map<string, string>; pageName: string }> { + try { + const response = await fetch(siteUrl, { + headers: { Accept: "text/html" }, + }); + + if (!response.ok) { + return { catalog: new Map(), pageName: fallbackName }; + } + + const pageHtml = await response.text(); + return { + catalog: parseComponentCatalog(pageHtml), + pageName: parsePageTitle(pageHtml, fallbackName), + }; + } catch { + return { catalog: new Map(), pageName: fallbackName }; + } +} + +function buildHistoryFromImpacts( + impacts: IncidentIoComponentImpact[], + componentId: string, + days = 90, +): DayStatus[] { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const dayMap = new Map<string, DayStatus["level"]>(); + + for (let i = 0; i < days; i++) { + const d = new Date(today); + d.setDate(d.getDate() - (days - 1 - i)); + dayMap.set(d.toISOString().slice(0, 10), "operational"); + } + + const now = new Date(); + + for (const impact of impacts) { + if (impact.component_id !== componentId) { + continue; + } + + const start = new Date(impact.start_at); + const end = impact.end_at ? new Date(impact.end_at) : now; + const level = impactToDayLevel(impact.status); + + const cursor = new Date(start); + cursor.setHours(0, 0, 0, 0); + const endDay = new Date(end); + endDay.setHours(0, 0, 0, 0); + + while (cursor <= endDay) { + const key = cursor.toISOString().slice(0, 10); + const existing = dayMap.get(key); + if ( + existing !== undefined && + DAY_LEVEL_SEVERITY[level] > DAY_LEVEL_SEVERITY[existing] + ) { + dayMap.set(key, level); + } + cursor.setDate(cursor.getDate() + 1); + } + } + + return Array.from(dayMap.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([date, level]) => ({ date, level })); +} + +function mapIncidents(incidents: IncidentIoIncident[]): StatusIncident[] { + return incidents + .filter((incident) => isActiveIncident(incident.status)) + .map((incident) => { + const affectedStatuses = + incident.affected_components?.map((component) => component.status) ?? + []; + const worst = worstStatus(affectedStatuses); + const latestUpdate = incident.updates?.[0]; + + return { + id: incident.id, + name: incident.name, + status: incident.status, + impact: impactToIncidentImpact(worst), + updatedAt: latestUpdate?.published_at ?? incident.published_at, + body: latestUpdate?.message_string, + affectedComponentIds: incident.affected_components?.map( + (component) => component.component_id, + ), + }; + }); +} + +function currentComponentStatuses( + incidents: IncidentIoIncident[], +): Map<string, IncidentIoImpactStatus> { + const statuses = new Map<string, IncidentIoImpactStatus>(); + + for (const incident of incidents) { + if (!isActiveIncident(incident.status)) { + continue; + } + + for (const component of incident.affected_components ?? []) { + const current = component.current_status ?? component.status; + const existing = statuses.get(component.component_id) ?? "operational"; + if (STATUS_SEVERITY[current] > STATUS_SEVERITY[existing]) { + statuses.set(component.component_id, current); + } + } + } + + return statuses; +} + +function computeOverallIndicator( + activeIncidents: StatusIncident[], + liveStatuses: Map<string, IncidentIoImpactStatus>, +): StatusIndicator { + if (activeIncidents.length === 0 && liveStatuses.size === 0) { + return "none"; + } + + if (liveStatuses.size > 0) { + return statusToIndicator(worstStatus([...liveStatuses.values()])); + } + + return "minor"; +} + +export const incidentIoAdapter: StatusAdapter = { + async detect(siteUrl: string): Promise<boolean> { + try { + const proxy = proxyBase(siteUrl); + const data = await fetchJson<ComponentImpactsResponse>( + componentImpactsUrl(proxy), + ); + return Array.isArray(data.component_impacts); + } catch { + return false; + } + }, + + async fetchSnapshot(siteUrl: string): Promise<StatusSnapshot> { + const normalized = normalizeSiteUrl(siteUrl); + const proxy = proxyBase(normalized); + const hostname = new URL(normalized).hostname; + const fetchedAt = new Date().toISOString(); + + try { + const [incidentsData, impactsData, pageMetadata] = await Promise.all([ + fetchJson<{ incidents: IncidentIoIncident[] }>(`${proxy}/incidents`), + fetchJson<ComponentImpactsResponse>(componentImpactsUrl(proxy)), + fetchPageMetadata(normalized, hostname), + ]); + + const { catalog, pageName } = pageMetadata; + const incidents = incidentsData.incidents ?? []; + const componentImpacts = impactsData.component_impacts ?? []; + const componentUptimes = impactsData.component_uptimes ?? []; + const liveStatuses = currentComponentStatuses(incidents); + const activeIncidents = mapIncidents(incidents); + + const uptimeEntries = componentUptimes.filter( + ( + entry, + ): entry is IncidentIoComponentUptime & { component_id: string } => + Boolean(entry.component_id), + ); + + const components = uptimeEntries.map((entry) => { + const status = + liveStatuses.get(entry.component_id) ?? ("operational" as const); + + return { + id: entry.component_id, + name: catalog.get(entry.component_id) ?? entry.component_id, + status: normalizeComponentStatus(status), + uptimePercent: parseFloat(entry.uptime), + historyDays: buildHistoryFromImpacts( + componentImpacts, + entry.component_id, + ), + }; + }); + + components.sort((a, b) => a.name.localeCompare(b.name)); + + const indicator = computeOverallIndicator(activeIncidents, liveStatuses); + const uptimePercent = averageComponentUptime(components); + + return { + pageName, + pageUrl: normalized, + overallDescription: overallDescription( + indicator, + activeIncidents.length, + ), + indicator, + components, + incidents: activeIncidents, + historyDays: buildPageHistoryFromComponents(components), + uptimePercent, + fetchedAt, + }; + } catch (error) { + return { + pageName: hostname, + pageUrl: normalized, + overallDescription: "Failed to fetch", + indicator: "none", + components: [], + incidents: [], + fetchedAt, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, +}; diff --git a/extensions/is-it-alive/src/adapters/index.ts b/extensions/is-it-alive/src/adapters/index.ts new file mode 100644 index 00000000000..5daf598dcf2 --- /dev/null +++ b/extensions/is-it-alive/src/adapters/index.ts @@ -0,0 +1,64 @@ +import type { SiteProvider, StatusAdapter, StatusSnapshot } from "@/types"; +import { isRailwayHost, normalizeSiteUrl } from "@/lib/url"; +import { betterstackAdapter } from "@/adapters/betterstack"; +import { incidentIoAdapter } from "@/adapters/incident-io"; +import { railwayAdapter } from "@/adapters/railway"; +import { statuspageAdapter } from "@/adapters/statuspage"; + +const adapters: Record<SiteProvider, StatusAdapter> = { + statuspage: statuspageAdapter, + railway: railwayAdapter, + incidentio: incidentIoAdapter, + betterstack: betterstackAdapter, +}; + +export function getAdapter(provider: SiteProvider): StatusAdapter { + return adapters[provider]; +} + +export async function detectProvider(siteUrl: string): Promise<SiteProvider> { + const normalized = normalizeSiteUrl(siteUrl); + + if (isRailwayHost(normalized)) { + return "railway"; + } + + const isIncidentIo = await incidentIoAdapter.detect?.(normalized); + if (isIncidentIo) { + return "incidentio"; + } + + const isBetterStack = await betterstackAdapter.detect?.(normalized); + if (isBetterStack) { + return "betterstack"; + } + + const isStatuspage = await statuspageAdapter.detect?.(normalized); + if (isStatuspage) { + return "statuspage"; + } + + throw new Error( + "Unsupported status page. Try Statuspage, Better Stack, incident.io, or status.railway.app", + ); +} + +export async function fetchSnapshot(site: { + url: string; + provider: SiteProvider; +}): Promise<StatusSnapshot> { + return getAdapter(site.provider).fetchSnapshot(site.url); +} + +export async function fetchAllSnapshots( + sites: Array<{ id: string; url: string; provider: SiteProvider }>, +): Promise<Record<string, StatusSnapshot>> { + const entries = await Promise.all( + sites.map(async (site) => { + const snapshot = await fetchSnapshot(site); + return [site.id, snapshot] as const; + }), + ); + + return Object.fromEntries(entries); +} diff --git a/extensions/is-it-alive/src/adapters/railway.ts b/extensions/is-it-alive/src/adapters/railway.ts new file mode 100644 index 00000000000..7336ec38882 --- /dev/null +++ b/extensions/is-it-alive/src/adapters/railway.ts @@ -0,0 +1,193 @@ +import type { + ComponentStatusValue, + DayStatus, + StatusAdapter, + StatusIncident, + StatusIndicator, + StatusSnapshot, +} from "@/types"; +import { normalizeSiteUrl } from "@/lib/url"; +import { overallDescription } from "@/lib/snapshot-text"; +import { + averageComponentUptime, + buildPageHistoryFromComponents, +} from "@/lib/uptime-chart"; +import { + RailwayStatusResponse, + RailwayComponent, + RailwayDay, + RailwayDayStatus, +} from "@/types/railway"; + +const RAILWAY_API = "https://status.railway.com/api/status"; +const RAILWAY_PAGE = "https://status.railway.com"; + +function railwayDayToLevel(status: RailwayDayStatus): DayStatus["level"] { + switch (status) { + case "operational": + return "operational"; + case "degraded": + return "degraded"; + case "major": + return "major"; + case "none": + return "unknown"; + default: + return "unknown"; + } +} + +function railwayDayToComponentStatus( + status: RailwayDayStatus, +): ComponentStatusValue | string { + switch (status) { + case "operational": + return "operational"; + case "degraded": + return "degraded_performance"; + case "major": + return "major_outage"; + default: + return "operational"; + } +} + +function flattenComponents( + entries: RailwayStatusResponse["entries"], +): RailwayComponent[] { + return entries + .filter((entry) => entry.type === "component") + .map((entry) => entry.data); +} + +function getComponentDays(component: RailwayComponent): RailwayDay[] { + return (component.months ?? []) + .flatMap((month) => month.days ?? []) + .sort((a, b) => a.date.localeCompare(b.date)); +} + +function getLatestDayStatus(component: RailwayComponent): RailwayDayStatus { + const today = new Date().toISOString().slice(0, 10); + const days = getComponentDays(component).filter( + (d) => d.date <= today && d.status !== "none", + ); + + if (days.length === 0) { + return "operational"; + } + + return days[days.length - 1].status; +} + +function buildComponentHistory(component: RailwayComponent): DayStatus[] { + const today = new Date().toISOString().slice(0, 10); + return getComponentDays(component) + .filter((d) => d.date <= today && d.status !== "none") + .slice(-90) + .map((d) => ({ date: d.date, level: railwayDayToLevel(d.status) })); +} + +function worstIndicator( + components: RailwayComponent[], + activeCount: number, +): StatusIndicator { + if (activeCount > 0) { + return "minor"; + } + + let worst: StatusIndicator = "none"; + + for (const component of components) { + const dayStatus = getLatestDayStatus(component); + if (dayStatus === "major") { + return "major"; + } + if (dayStatus === "degraded" && worst === "none") { + worst = "minor"; + } + } + + return worst; +} + +function mapActiveIncidents( + incidents: RailwayStatusResponse["activeIncidents"], +): StatusIncident[] { + return incidents.map((incident) => ({ + id: incident.id, + name: incident.title, + status: incident.status.toLowerCase(), + impact: "minor", + updatedAt: incident.createdAt, + body: incident.updates?.[0]?.message, + affectedComponentIds: incident.components?.map((c) => c.id), + })); +} + +export const railwayAdapter: StatusAdapter = { + async detect(siteUrl: string): Promise<boolean> { + try { + const host = new URL(normalizeSiteUrl(siteUrl)).hostname; + return host === "status.railway.app" || host === "status.railway.com"; + } catch { + return false; + } + }, + + async fetchSnapshot(): Promise<StatusSnapshot> { + const fetchedAt = new Date().toISOString(); + + try { + const response = await fetch(RAILWAY_API, { + headers: { Accept: "application/json" }, + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + const data = (await response.json()) as RailwayStatusResponse; + const components = flattenComponents(data.entries); + + const mappedComponents = components.map((component) => { + const latest = getLatestDayStatus(component); + const uptime = component.uptimePercentage?.replace("%", ""); + + return { + id: component.id, + name: component.name, + status: railwayDayToComponentStatus(latest), + uptimePercent: uptime ? parseFloat(uptime) : undefined, + historyDays: buildComponentHistory(component), + }; + }); + + const incidents = mapActiveIncidents(data.activeIncidents); + const indicator = worstIndicator(components, incidents.length); + const uptimePercent = averageComponentUptime(mappedComponents); + + return { + pageName: "Railway", + pageUrl: RAILWAY_PAGE, + overallDescription: overallDescription(indicator, incidents.length), + indicator, + components: mappedComponents, + incidents, + historyDays: buildPageHistoryFromComponents(mappedComponents), + uptimePercent, + fetchedAt, + }; + } catch (error) { + return { + pageName: "Railway", + pageUrl: RAILWAY_PAGE, + overallDescription: "Failed to fetch", + indicator: "none", + components: [], + incidents: [], + fetchedAt, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, +}; diff --git a/extensions/is-it-alive/src/adapters/statuspage.ts b/extensions/is-it-alive/src/adapters/statuspage.ts new file mode 100644 index 00000000000..4ecf7e09886 --- /dev/null +++ b/extensions/is-it-alive/src/adapters/statuspage.ts @@ -0,0 +1,204 @@ +import type { + ComponentStatusValue, + DayStatus, + StatusAdapter, + StatusIncident, + StatusSnapshot, +} from "@/types"; +import { getOrigin, normalizeSiteUrl } from "@/lib/url"; +import { fetchJson } from "@/lib/fetch-json"; +import { + averageComponentUptime, + buildPageHistoryFromComponents, + calcUptimePercent, +} from "@/lib/uptime-chart"; +import { StatuspageSummary, StatuspageIncident } from "@/types/statuspage"; + +function isActiveStatuspageIncident(incident: { status: string }): boolean { + return incident.status !== "resolved" && incident.status !== "postmortem"; +} + +function resolveActiveIncidents( + summaryIncidents: StatuspageSummary["incidents"], + allIncidents: StatuspageIncident[], +): Array< + StatuspageIncident | NonNullable<StatuspageSummary["incidents"]>[number] +> { + const incidents = summaryIncidents ?? allIncidents; + return incidents.filter(isActiveStatuspageIncident); +} + +function mapIncidents( + incidents: Array< + StatuspageIncident | NonNullable<StatuspageSummary["incidents"]>[number] + >, +): StatusIncident[] { + return incidents.map((incident) => ({ + id: incident.id, + name: incident.name, + status: incident.status, + impact: incident.impact, + updatedAt: incident.updated_at, + body: incident.incident_updates?.[0]?.body, + affectedComponentIds: + incident.components?.map((c) => c.id) ?? + incident.incident_updates?.[0]?.affected_components?.map((c) => c.code), + })); +} + +function impactToLevel(impact: string): DayStatus["level"] { + switch (impact) { + case "none": + return "operational"; + case "minor": + return "degraded"; + case "major": + return "partial"; + case "critical": + return "major"; + default: + return "degraded"; + } +} + +function incidentAffectsComponent( + incident: StatuspageIncident, + componentId: string, +): boolean { + if (incident.components?.some((c) => c.id === componentId)) { + return true; + } + + return ( + incident.incident_updates?.some((update) => + update.affected_components?.some((c) => c.code === componentId), + ) ?? false + ); +} + +function buildHistoryFromIncidents( + incidents: StatuspageIncident[], + days = 90, +): DayStatus[] { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + const dayMap = new Map<string, DayStatus["level"]>(); + + for (let i = 0; i < days; i++) { + const d = new Date(today); + d.setDate(d.getDate() - (days - 1 - i)); + dayMap.set(d.toISOString().slice(0, 10), "operational"); + } + + const severity: Record<DayStatus["level"], number> = { + operational: 0, + degraded: 1, + partial: 2, + major: 3, + unknown: 0, + }; + + for (const incident of incidents) { + const start = new Date(incident.created_at); + const end = incident.resolved_at ? new Date(incident.resolved_at) : today; + const level = impactToLevel(incident.impact); + + const cursor = new Date(start); + cursor.setHours(0, 0, 0, 0); + const endDay = new Date(end); + endDay.setHours(0, 0, 0, 0); + + while (cursor <= endDay) { + const key = cursor.toISOString().slice(0, 10); + const existing = dayMap.get(key); + if (existing !== undefined && severity[level] > severity[existing]) { + dayMap.set(key, level); + } + cursor.setDate(cursor.getDate() + 1); + } + } + + return Array.from(dayMap.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([date, level]) => ({ date, level })); +} + +export const statuspageAdapter: StatusAdapter = { + async detect(siteUrl: string): Promise<boolean> { + try { + const origin = getOrigin(normalizeSiteUrl(siteUrl)); + await fetchJson<StatuspageSummary>(`${origin}/api/v2/summary.json`); + return true; + } catch { + return false; + } + }, + + async fetchSnapshot(siteUrl: string): Promise<StatusSnapshot> { + const normalized = normalizeSiteUrl(siteUrl); + const origin = getOrigin(normalized); + const fetchedAt = new Date().toISOString(); + + try { + const [summary, allIncidents] = await Promise.all([ + fetchJson<StatuspageSummary>(`${origin}/api/v2/summary.json`), + fetchJson<{ incidents: StatuspageIncident[] }>( + `${origin}/api/v2/incidents.json`, + ).catch(() => ({ incidents: [] as StatuspageIncident[] })), + ]); + + const historyDays = buildHistoryFromIncidents( + allIncidents.incidents ?? [], + ); + + const components = (summary.components ?? []) + .filter((c) => !c.group) + .map((c) => ({ + id: c.id, + name: c.name, + status: c.status as ComponentStatusValue, + historyDays: buildHistoryFromIncidents( + (allIncidents.incidents ?? []).filter((incident) => + incidentAffectsComponent(incident, c.id), + ), + ), + })); + + const pageHistoryDays = buildPageHistoryFromComponents(components); + const uptimePercent = + averageComponentUptime(components) ?? + (pageHistoryDays.length > 0 + ? calcUptimePercent(pageHistoryDays) + : undefined); + + const activeIncidents = resolveActiveIncidents( + summary.incidents, + allIncidents.incidents ?? [], + ); + + return { + pageName: summary.page.name, + pageUrl: summary.page.url || normalized, + overallDescription: summary.status.description, + indicator: summary.status.indicator, + components, + incidents: mapIncidents(activeIncidents), + historyDays: pageHistoryDays.length > 0 ? pageHistoryDays : historyDays, + uptimePercent, + fetchedAt, + }; + } catch (error) { + return { + pageName: new URL(normalized).hostname, + pageUrl: normalized, + overallDescription: "Failed to fetch", + indicator: "none", + components: [], + incidents: [], + fetchedAt, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, +}; diff --git a/extensions/is-it-alive/src/check-status-pages.tsx b/extensions/is-it-alive/src/check-status-pages.tsx new file mode 100644 index 00000000000..1a085078d94 --- /dev/null +++ b/extensions/is-it-alive/src/check-status-pages.tsx @@ -0,0 +1,167 @@ +import { + Action, + ActionPanel, + Color, + Icon, + List, + showToast, + Toast, +} from "@raycast/api"; +import { useCachedPromise } from "@raycast/utils"; +import { fetchAllSnapshots } from "@/adapters"; +import { SiteDetail } from "@/components/site-detail"; +import { SiteForm } from "@/components/site-form"; +import { useSites } from "@/hooks/use-sites"; +import { indicatorListIcon } from "@/lib/status-colors"; +import type { MonitoredSite, StatusSnapshot } from "@/types"; + +export default function Command() { + const { + sites, + isLoading: isLoadingSites, + addSite, + deleteSite, + updateSite, + } = useSites(); + + const { + data: snapshots, + isLoading: isLoadingSnapshots, + error: snapshotsError, + revalidate, + } = useCachedPromise( + async (siteList: MonitoredSite[]) => { + if (siteList.length === 0) { + return {} as Record<string, StatusSnapshot>; + } + return fetchAllSnapshots(siteList); + }, + [sites], + { keepPreviousData: true }, + ); + + const isLoading = isLoadingSites || isLoadingSnapshots; + + async function handleDelete(site: MonitoredSite) { + await deleteSite(site.id); + await showToast({ style: Toast.Style.Success, title: "Site removed" }); + } + + return ( + <List + isLoading={isLoading} + searchBarPlaceholder="Search monitored sites..." + > + <List.EmptyView + title="No sites yet" + description="Add a status page URL to monitor services like Claude, GitHub, or Railway." + actions={ + <ActionPanel> + <Action.Push + title="Add Site" + icon={Icon.Plus} + target={ + <SiteForm + onSave={async (values) => { + await addSite(values); + }} + /> + } + /> + </ActionPanel> + } + /> + + {sites.map((site) => { + const snapshot = snapshots?.[site.id]; + const hasError = Boolean(snapshot?.error || snapshotsError); + const icon = hasError + ? { source: Icon.QuestionMark, tintColor: Color.SecondaryText } + : indicatorListIcon(snapshot?.indicator ?? "unknown"); + + const subtitle = snapshot?.error + ? "Failed to fetch — retry" + : snapshotsError + ? "Failed to load — retry" + : (snapshot?.overallDescription ?? "Loading..."); + + return ( + <List.Item + key={site.id} + title={site.name} + subtitle={subtitle} + icon={icon} + accessories={[ + ...(snapshot?.incidents?.length + ? [ + { + icon: Icon.Warning, + tooltip: `${snapshot.incidents.length} active incident(s)`, + }, + ] + : []), + ...(snapshot?.fetchedAt + ? [ + { + date: new Date(snapshot.fetchedAt), + tooltip: "Last fetched", + }, + ] + : []), + ]} + actions={ + <ActionPanel> + {snapshot && !hasError ? ( + <Action.Push + title="View Status Details" + icon={Icon.Eye} + target={<SiteDetail snapshot={snapshot} />} + /> + ) : ( + <Action + title="Refresh" + icon={Icon.ArrowClockwise} + onAction={revalidate} + /> + )} + <Action.Push + title="Add Site" + icon={Icon.Plus} + target={ + <SiteForm + onSave={async (values) => { + await addSite(values); + }} + /> + } + /> + <Action.Push + title="Edit Site" + icon={Icon.Pencil} + target={ + <SiteForm + site={site} + onSave={(values) => updateSite(site.id, values)} + /> + } + /> + <Action + title="Delete Site" + shortcut={{ modifiers: ["ctrl"], key: "delete" }} + icon={Icon.Trash} + style={Action.Style.Destructive} + onAction={() => handleDelete(site)} + /> + <Action + title="Refresh All" + icon={Icon.ArrowClockwise} + onAction={revalidate} + /> + </ActionPanel> + } + /> + ); + })} + </List> + ); +} diff --git a/extensions/is-it-alive/src/components/site-detail.tsx b/extensions/is-it-alive/src/components/site-detail.tsx new file mode 100644 index 00000000000..88b93b57936 --- /dev/null +++ b/extensions/is-it-alive/src/components/site-detail.tsx @@ -0,0 +1,176 @@ +import { Action, ActionPanel, Color, Icon, List } from "@raycast/api"; +import type { StatusSnapshot } from "@/types"; +import { + componentStatusLabel, + componentStatusListIcon, + indicatorListIcon, +} from "@/lib/status-colors"; +import { + buildUptimeDetailMarkdown, + averageComponentUptime, + resolveUptimePercent, +} from "@/lib/uptime-chart"; +import { formatUptimePercent } from "@/lib/snapshot-text"; + +interface SiteDetailProps { + snapshot: StatusSnapshot; +} + +function buildIncidentMarkdown( + incident: StatusSnapshot["incidents"][number], +): string { + return `### ${incident.name}\n\n${incident.body ?? incident.status}`; +} + +function buildOverviewMarkdown(snapshot: StatusSnapshot): string { + const overviewUptime = + snapshot.uptimePercent ?? averageComponentUptime(snapshot.components); + const uptimeMarkdown = buildUptimeDetailMarkdown( + snapshot.historyDays, + overviewUptime, + ); + const incidentsMarkdown = + snapshot.incidents.length > 0 + ? snapshot.incidents.map(buildIncidentMarkdown).join("\n\n---\n\n") + : "No active incidents."; + + if (!uptimeMarkdown) { + return incidentsMarkdown; + } + + return `${uptimeMarkdown}\n\n---\n\n${incidentsMarkdown}`; +} + +export function SiteDetail({ snapshot }: SiteDetailProps) { + const overviewIcon = snapshot.error + ? { source: Icon.QuestionMark, tintColor: Color.SecondaryText } + : indicatorListIcon(snapshot.indicator); + + return ( + <List navigationTitle={snapshot.pageName} isShowingDetail> + <List.Section title="Overview"> + <List.Item + title={snapshot.overallDescription} + subtitle={ + snapshot.error ?? `${snapshot.components.length} components` + } + icon={overviewIcon} + detail={ + <List.Item.Detail + markdown={buildOverviewMarkdown(snapshot)} + metadata={ + <List.Item.Detail.Metadata> + <List.Item.Detail.Metadata.Label + title="Status" + text={snapshot.overallDescription} + /> + <List.Item.Detail.Metadata.Separator /> + <List.Item.Detail.Metadata.Label + title="Uptime" + text={formatUptimePercent(snapshot.uptimePercent)} + /> + <List.Item.Detail.Metadata.Separator /> + <List.Item.Detail.Metadata.Label + title="Fetched" + text={new Date(snapshot.fetchedAt).toLocaleString()} + /> + </List.Item.Detail.Metadata> + } + /> + } + actions={ + <ActionPanel> + <Action.OpenInBrowser + url={snapshot.pageUrl} + title="Open Status Page" + /> + </ActionPanel> + } + /> + </List.Section> + + {snapshot.incidents.length > 0 && ( + <List.Section title="Active Incidents"> + {snapshot.incidents.map((incident) => ( + <List.Item + key={incident.id} + title={incident.name} + subtitle={incident.status} + icon={{ source: Icon.Warning, tintColor: Color.Yellow }} + detail={ + <List.Item.Detail + markdown={buildIncidentMarkdown(incident)} + metadata={ + <List.Item.Detail.Metadata> + <List.Item.Detail.Metadata.Label + title="Status" + text={incident.status} + /> + </List.Item.Detail.Metadata> + } + /> + } + /> + ))} + </List.Section> + )} + + <List.Section title="Components"> + {snapshot.components.map((component) => { + const uptime = resolveUptimePercent( + component.historyDays, + component.uptimePercent, + ); + const uptimeMarkdown = buildUptimeDetailMarkdown( + component.historyDays, + component.uptimePercent, + ); + + return ( + <List.Item + key={component.id} + title={component.name} + icon={componentStatusListIcon(component.status)} + accessories={ + uptime !== null + ? [{ text: formatUptimePercent(uptime) }] + : undefined + } + detail={ + <List.Item.Detail + markdown={uptimeMarkdown} + metadata={ + <List.Item.Detail.Metadata> + <List.Item.Detail.Metadata.Label + title="Status" + text={componentStatusLabel(component.status)} + /> + <List.Item.Detail.Metadata.Separator /> + <List.Item.Detail.Metadata.Label + title="Uptime" + text={formatUptimePercent(uptime)} + /> + <List.Item.Detail.Metadata.Separator /> + <List.Item.Detail.Metadata.Label + title="Fetched" + text={new Date(snapshot.fetchedAt).toLocaleString()} + /> + </List.Item.Detail.Metadata> + } + /> + } + actions={ + <ActionPanel> + <Action.OpenInBrowser + url={snapshot.pageUrl} + title="Open Status Page" + /> + </ActionPanel> + } + /> + ); + })} + </List.Section> + </List> + ); +} diff --git a/extensions/is-it-alive/src/components/site-form.tsx b/extensions/is-it-alive/src/components/site-form.tsx new file mode 100644 index 00000000000..e8f83bf0780 --- /dev/null +++ b/extensions/is-it-alive/src/components/site-form.tsx @@ -0,0 +1,92 @@ +import { + Action, + ActionPanel, + Form, + Icon, + showToast, + Toast, + useNavigation, +} from "@raycast/api"; +import { useState } from "react"; +import { detectProvider, fetchSnapshot } from "@/adapters"; +import type { MonitoredSite } from "@/types"; +import { normalizeSiteUrl } from "@/lib/url"; + +interface SiteFormValues { + url: string; + name: string; +} + +interface SiteFormProps { + site?: MonitoredSite; + onSave: (values: { + name: string; + url: string; + provider: MonitoredSite["provider"]; + }) => Promise<void>; +} + +export function SiteForm({ site, onSave }: SiteFormProps) { + const { pop } = useNavigation(); + const [isSubmitting, setIsSubmitting] = useState(false); + + async function handleSubmit(values: SiteFormValues) { + setIsSubmitting(true); + + try { + const url = normalizeSiteUrl(values.url); + const provider = await detectProvider(url); + const snapshot = await fetchSnapshot({ url, provider }); + + if (snapshot.error) { + throw new Error(snapshot.error); + } + + const name = values.name.trim() || snapshot.pageName; + + await onSave({ name, url, provider }); + await showToast({ + style: Toast.Style.Success, + title: site ? "Site updated" : "Site added", + }); + pop(); + } catch (error) { + await showToast({ + style: Toast.Style.Failure, + title: site ? "Failed to update site" : "Failed to add site", + message: error instanceof Error ? error.message : "Unknown error", + }); + } finally { + setIsSubmitting(false); + } + } + + return ( + <Form + isLoading={isSubmitting} + actions={ + <ActionPanel> + <Action.SubmitForm + title={site ? "Save Site" : "Add Site"} + icon={Icon.Plus} + onSubmit={handleSubmit} + /> + </ActionPanel> + } + > + <Form.TextField + id="url" + title="Status Page URL" + placeholder="https://status.claude.com" + defaultValue={site?.url} + /> + <Form.TextField + id="name" + title="Display Name" + info="Optional — auto-filled from the status page if left empty" + placeholder="Claude" + defaultValue={site?.name} + /> + </Form> + ); +} diff --git a/extensions/is-it-alive/src/hooks/use-sites.ts b/extensions/is-it-alive/src/hooks/use-sites.ts new file mode 100644 index 00000000000..fcce1006d84 --- /dev/null +++ b/extensions/is-it-alive/src/hooks/use-sites.ts @@ -0,0 +1,69 @@ +import { useCallback } from "react"; +import { useLocalStorage } from "@raycast/utils"; +import type { MonitoredSite, SiteProvider } from "@/types"; + +const STORAGE_KEY = "sites"; + +function createId(): string { + return crypto.randomUUID(); +} + +export function useSites() { + const { + value: sites, + setValue: setSites, + isLoading, + } = useLocalStorage<MonitoredSite[]>(STORAGE_KEY, []); + + const addSite = useCallback( + async (input: { name: string; url: string; provider: SiteProvider }) => { + const next: MonitoredSite = { + id: createId(), + name: input.name, + url: input.url, + provider: input.provider, + createdAt: new Date().toISOString(), + }; + + await setSites([...(sites ?? []), next]); + return next; + }, + [setSites, sites], + ); + + const updateSite = useCallback( + async ( + id: string, + input: { name: string; url: string; provider: SiteProvider }, + ) => { + await setSites( + (sites ?? []).map((site) => + site.id === id + ? { + ...site, + name: input.name, + url: input.url, + provider: input.provider, + } + : site, + ), + ); + }, + [setSites, sites], + ); + + const deleteSite = useCallback( + async (id: string) => { + await setSites((sites ?? []).filter((site) => site.id !== id)); + }, + [setSites, sites], + ); + + return { + sites: sites ?? [], + isLoading, + addSite, + updateSite, + deleteSite, + }; +} diff --git a/extensions/is-it-alive/src/lib/fetch-json.ts b/extensions/is-it-alive/src/lib/fetch-json.ts new file mode 100644 index 00000000000..00ad92032d1 --- /dev/null +++ b/extensions/is-it-alive/src/lib/fetch-json.ts @@ -0,0 +1,16 @@ +export async function fetchJson<T>(url: string): Promise<T> { + const response = await fetch(url, { + headers: { Accept: "application/json" }, + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + const contentType = response.headers.get("content-type") ?? ""; + if (!contentType.includes("application/json")) { + throw new Error("Not a JSON status page"); + } + + return response.json() as Promise<T>; +} diff --git a/extensions/is-it-alive/src/lib/snapshot-text.ts b/extensions/is-it-alive/src/lib/snapshot-text.ts new file mode 100644 index 00000000000..24ac3542800 --- /dev/null +++ b/extensions/is-it-alive/src/lib/snapshot-text.ts @@ -0,0 +1,31 @@ +import type { StatusIndicator } from "@/types"; + +export function formatUptimePercent(uptime: number | null | undefined): string { + if (uptime === null || uptime === undefined || Number.isNaN(uptime)) { + return "Unknown"; + } + + return `${uptime.toFixed(2)}% uptime`; +} + +export function overallDescription( + indicator: StatusIndicator, + activeIncidents: number, +): string { + if (activeIncidents > 0) { + return `${activeIncidents} active incident${activeIncidents === 1 ? "" : "s"}`; + } + + switch (indicator) { + case "none": + return "All Systems Operational"; + case "minor": + return "Degraded Performance"; + case "major": + return "Partial Outage"; + case "critical": + return "Major Outage"; + default: + return "Unknown"; + } +} diff --git a/extensions/is-it-alive/src/lib/status-colors.ts b/extensions/is-it-alive/src/lib/status-colors.ts new file mode 100644 index 00000000000..926605712e9 --- /dev/null +++ b/extensions/is-it-alive/src/lib/status-colors.ts @@ -0,0 +1,77 @@ +import { Color, Icon } from "@raycast/api"; +import type { Image } from "@raycast/api"; +import type { ComponentStatusValue, StatusIndicator } from "@/types"; + +export function indicatorColor(indicator: StatusIndicator | string): Color { + switch (indicator) { + case "none": + return Color.Green; + case "minor": + return Color.Yellow; + case "major": + return Color.Orange; + case "critical": + return Color.Red; + default: + return Color.SecondaryText; + } +} + +export function indicatorListIcon( + indicator: StatusIndicator | string, +): Image.ImageLike { + switch (indicator) { + case "none": + return { source: Icon.CheckCircle, tintColor: Color.Green }; + case "minor": + return { source: Icon.Warning, tintColor: Color.Yellow }; + case "major": + return { source: Icon.Warning, tintColor: Color.Orange }; + case "critical": + return { source: Icon.XMarkCircle, tintColor: Color.Red }; + default: + return { source: Icon.QuestionMark, tintColor: Color.SecondaryText }; + } +} + +export function componentStatusLabel( + status: ComponentStatusValue | string, +): string { + switch (status) { + case "operational": + return "Operational"; + case "degraded_performance": + return "Degraded Performance"; + case "partial_outage": + return "Partial Outage"; + case "major_outage": + return "Major Outage"; + case "full_outage": + return "Major Outage"; + case "under_maintenance": + return "Under Maintenance"; + default: + return status.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); + } +} + +export function componentStatusListIcon( + status: ComponentStatusValue | string, +): Image.ImageLike { + switch (status) { + case "operational": + return { source: Icon.CheckCircle, tintColor: Color.Green }; + case "degraded_performance": + return { source: Icon.Warning, tintColor: Color.Yellow }; + case "partial_outage": + return { source: Icon.Warning, tintColor: Color.Orange }; + case "major_outage": + return { source: Icon.XMarkCircle, tintColor: Color.Red }; + case "full_outage": + return { source: Icon.XMarkCircle, tintColor: Color.Red }; + case "under_maintenance": + return { source: Icon.Warning, tintColor: Color.Blue }; + default: + return { source: Icon.QuestionMark, tintColor: Color.SecondaryText }; + } +} diff --git a/extensions/is-it-alive/src/lib/uptime-chart.ts b/extensions/is-it-alive/src/lib/uptime-chart.ts new file mode 100644 index 00000000000..4d0153011d9 --- /dev/null +++ b/extensions/is-it-alive/src/lib/uptime-chart.ts @@ -0,0 +1,156 @@ +import type { DayStatus } from "@/types"; + +const LEVEL_COLOR: Record<DayStatus["level"], string> = { + operational: "#3fb950", + degraded: "#e3b341", + partial: "#f0883e", + major: "#f85149", + unknown: "#6e7681", +}; + +export function calcUptimePercent(days: DayStatus[]): number { + if (days.length === 0) { + return 100; + } + + const operational = days.filter((d) => d.level === "operational").length; + return (operational / days.length) * 100; +} + +export function resolveUptimePercent( + days: DayStatus[] | undefined, + uptimePercent?: number, +): number | null { + if (uptimePercent !== undefined && !Number.isNaN(uptimePercent)) { + return uptimePercent; + } + + if (!days?.length) { + return null; + } + + return calcUptimePercent(days); +} + +export function averageComponentUptime( + components: Array<{ uptimePercent?: number; historyDays?: DayStatus[] }>, +): number | undefined { + const values = components + .map((component) => { + if ( + component.uptimePercent !== undefined && + !Number.isNaN(component.uptimePercent) + ) { + return component.uptimePercent; + } + + if (component.historyDays?.length) { + return calcUptimePercent(component.historyDays); + } + + return undefined; + }) + .filter((value): value is number => value !== undefined); + + if (values.length === 0) { + return undefined; + } + + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +function aggregatePageDayLevel( + levels: DayStatus["level"][], +): DayStatus["level"] { + const affected = levels.filter((level) => level !== "operational").length; + const ratio = affected / levels.length; + + if (ratio === 0) { + return "operational"; + } + if (ratio < 0.3) { + return "degraded"; + } + if (ratio < 0.6) { + return "partial"; + } + return "major"; +} + +export function buildPageHistoryFromComponents( + components: Array<{ historyDays?: DayStatus[] }>, +): DayStatus[] { + const histories = components + .map((component) => component.historyDays ?? []) + .filter((history) => history.length > 0); + + if (histories.length === 0) { + return []; + } + + const levelsByDate = new Map<string, DayStatus["level"][]>(); + + for (const history of histories) { + for (const day of history) { + const levels = levelsByDate.get(day.date) ?? []; + levels.push(day.level); + levelsByDate.set(day.date, levels); + } + } + + return Array.from(levelsByDate.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([date, levels]) => ({ + date, + level: aggregatePageDayLevel(levels), + })); +} + +export function buildUptimeChartMarkdown( + days: DayStatus[], + width = 540, + height = 32, +): string { + if (days.length === 0) { + return ""; + } + + const gap = 2; + const barWidth = (width - gap * (days.length - 1)) / days.length; + const radius = 2; + + const rects = days + .map((day, index) => { + const x = index * (barWidth + gap); + const color = LEVEL_COLOR[day.level]; + return `<rect x="${x.toFixed(1)}" y="0" width="${barWidth.toFixed(1)}" height="${height}" rx="${radius}" fill="${color}" />`; + }) + .join(""); + + const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${rects}</svg>`; + const base64 = Buffer.from(svg).toString("base64"); + + return `![uptime chart](data:image/svg+xml;base64,${base64})`; +} + +export function buildUptimeDetailMarkdown( + days: DayStatus[] | undefined, + uptimePercent?: number, +): string | undefined { + if (!days?.length) { + return undefined; + } + + const chart = buildUptimeChartMarkdown(days); + const uptime = resolveUptimePercent(days, uptimePercent); + const dayCount = days.length; + + if (uptime === null) { + return chart; + } + + return [ + chart, + `Past ${dayCount} days — **${uptime.toFixed(2)}% uptime**`, + ].join("\n\n"); +} diff --git a/extensions/is-it-alive/src/lib/url.ts b/extensions/is-it-alive/src/lib/url.ts new file mode 100644 index 00000000000..82b1843f5f2 --- /dev/null +++ b/extensions/is-it-alive/src/lib/url.ts @@ -0,0 +1,38 @@ +export const RAILWAY_HOSTS = new Set([ + "status.railway.app", + "status.railway.com", +]); + +export function normalizeSiteUrl(input: string): string { + const trimmed = input.trim(); + if (!trimmed) { + throw new Error("URL is required"); + } + + const withProtocol = /^https?:\/\//i.test(trimmed) + ? trimmed + : `https://${trimmed}`; + const url = new URL(withProtocol); + + if (!["http:", "https:"].includes(url.protocol)) { + throw new Error("Only HTTP(S) URLs are supported"); + } + + url.hash = ""; + url.search = ""; + const normalizedPath = url.pathname.replace(/\/+$/, "") || ""; + + return normalizedPath ? `${url.origin}${normalizedPath}` : url.origin; +} + +export function getOrigin(siteUrl: string): string { + return new URL(siteUrl).origin; +} + +export function isRailwayHost(siteUrl: string): boolean { + try { + return RAILWAY_HOSTS.has(new URL(siteUrl).hostname); + } catch { + return false; + } +} diff --git a/extensions/is-it-alive/src/types/betterstack.ts b/extensions/is-it-alive/src/types/betterstack.ts new file mode 100644 index 00000000000..b159d8cf18b --- /dev/null +++ b/extensions/is-it-alive/src/types/betterstack.ts @@ -0,0 +1,106 @@ +export type BetterStackAggregateState = + | "operational" + | "degraded" + | "downtime" + | "maintenance" + | "resolved"; + +export type BetterStackResourceStatus = + | "operational" + | "degraded" + | "downtime" + | "maintenance" + | "not_monitored"; + +export type BetterStackReportType = "manual" | "automatic" | "maintenance"; + +export interface BetterStackStatusHistoryDay { + day: string; + status: BetterStackResourceStatus; + downtime_duration: number; + maintenance_duration: number; +} + +export interface BetterStackAffectedResource { + status_page_resource_id: string; + status: BetterStackResourceStatus | "resolved"; +} + +export interface BetterStackIndexResponse { + data: { + id: string; + type: "status_page"; + attributes: { + company_name: string; + company_url: string | null; + contact_url: string | null; + logo_url: string | null; + timezone: string; + subdomain: string; + custom_domain: string | null; + announcement: string | null; + aggregate_state: BetterStackAggregateState; + created_at: string; + updated_at: string; + }; + relationships: { + sections: { data: Array<{ id: string; type: "status_page_section" }> }; + resources: { data: Array<{ id: string; type: "status_page_resource" }> }; + status_reports: { data: Array<{ id: string; type: "status_report" }> }; + }; + }; + included: BetterStackIncludedItem[]; +} + +export type BetterStackIncludedItem = + | { + id: string; + type: "status_page_section"; + attributes: { + name: string; + position: number; + }; + } + | { + id: string; + type: "status_page_resource"; + attributes: { + status_page_section_id: number; + resource_id: number; + resource_type: string; + public_name: string; + explanation: string; + position: number; + availability: number; + status: BetterStackResourceStatus; + status_history: BetterStackStatusHistoryDay[]; + }; + } + | { + id: string; + type: "status_report"; + attributes: { + title: string; + report_type: BetterStackReportType; + starts_at: string; + ends_at: string | null; + affected_resources: BetterStackAffectedResource[]; + aggregate_state: BetterStackAggregateState; + }; + relationships: { + status_updates: { + data: Array<{ id: string; type: "status_update" }>; + }; + }; + } + | { + id: string; + type: "status_update"; + attributes: { + message: string; + published_at: string; + published_at_timezone: string; + notify_subscribers: boolean; + affected_resources: BetterStackAffectedResource[]; + }; + }; diff --git a/extensions/is-it-alive/src/types/incident-io.ts b/extensions/is-it-alive/src/types/incident-io.ts new file mode 100644 index 00000000000..a2169d7c1c6 --- /dev/null +++ b/extensions/is-it-alive/src/types/incident-io.ts @@ -0,0 +1,72 @@ +import { DayStatus } from "@/types"; + +export type IncidentIoImpactStatus = + | "operational" + | "degraded_performance" + | "partial_outage" + | "full_outage"; + +export interface IncidentIoComponentImpact { + start_at: string; + end_at: string | null; + component_id: string; + status: IncidentIoImpactStatus; + status_page_incident_id: string; +} + +export interface IncidentIoComponentUptime { + component_id?: string; + uptime: string; + status_page_component_group_id?: string; +} + +export interface IncidentIoIncidentLink { + id: string; + name: string; + status: string; + published_at: string; +} + +export interface IncidentIoIncidentUpdate { + published_at: string; + message_string?: string; + to_status?: string; + component_statuses?: Array<{ + component_id: string; + status: IncidentIoImpactStatus; + }>; +} + +export interface IncidentIoIncident { + id: string; + name: string; + status: string; + published_at: string; + affected_components?: Array<{ + component_id: string; + status: IncidentIoImpactStatus; + current_status: IncidentIoImpactStatus; + }>; + updates?: IncidentIoIncidentUpdate[]; +} + +export interface ComponentImpactsResponse { + incident_links: IncidentIoIncidentLink[]; + component_impacts: IncidentIoComponentImpact[]; + component_uptimes: IncidentIoComponentUptime[]; +} + +export const STATUS_SEVERITY: Record<IncidentIoImpactStatus, number> = { + operational: 0, + degraded_performance: 1, + partial_outage: 2, + full_outage: 3, +}; + +export const DAY_LEVEL_SEVERITY: Record<DayStatus["level"], number> = { + operational: 0, + degraded: 1, + partial: 2, + major: 3, + unknown: 0, +}; diff --git a/extensions/is-it-alive/src/types/index.ts b/extensions/is-it-alive/src/types/index.ts new file mode 100644 index 00000000000..ceedd36889f --- /dev/null +++ b/extensions/is-it-alive/src/types/index.ts @@ -0,0 +1,63 @@ +export type SiteProvider = + | "statuspage" + | "railway" + | "incidentio" + | "betterstack"; + +export type StatusIndicator = "none" | "minor" | "major" | "critical"; + +export type ComponentStatusValue = + | "operational" + | "degraded_performance" + | "partial_outage" + | "major_outage" + | "under_maintenance"; + +export interface MonitoredSite { + id: string; + name: string; + url: string; + provider: SiteProvider; + createdAt: string; +} + +export interface ComponentStatus { + id: string; + name: string; + status: ComponentStatusValue | string; + uptimePercent?: number; + historyDays?: DayStatus[]; +} + +export interface StatusIncident { + id: string; + name: string; + status: string; + impact: string; + updatedAt: string; + body?: string; + affectedComponentIds?: string[]; +} + +export interface DayStatus { + date: string; + level: "operational" | "degraded" | "partial" | "major" | "unknown"; +} + +export interface StatusSnapshot { + pageName: string; + pageUrl: string; + overallDescription: string; + indicator: StatusIndicator; + components: ComponentStatus[]; + incidents: StatusIncident[]; + historyDays?: DayStatus[]; + uptimePercent?: number; + fetchedAt: string; + error?: string; +} + +export interface StatusAdapter { + fetchSnapshot(siteUrl: string): Promise<StatusSnapshot>; + detect?(siteUrl: string): Promise<boolean>; +} diff --git a/extensions/is-it-alive/src/types/railway.ts b/extensions/is-it-alive/src/types/railway.ts new file mode 100644 index 00000000000..f236ba38f8a --- /dev/null +++ b/extensions/is-it-alive/src/types/railway.ts @@ -0,0 +1,27 @@ +export type RailwayDayStatus = "operational" | "degraded" | "major" | "none"; + +export interface RailwayDay { + date: string; + status: RailwayDayStatus; + incidents: Array<{ title: string; slug: string; durationMinutes: number }>; +} + +export interface RailwayComponent { + id: string; + name: string; + uptimePercentage?: string; + months?: Array<{ days: RailwayDay[] }>; +} + +export interface RailwayStatusResponse { + entries: Array<{ type: "component" | "group"; data: RailwayComponent }>; + activeIncidents: Array<{ + id: string; + title: string; + status: string; + createdAt: string; + updates?: Array<{ message: string }>; + components?: Array<{ id: string; name: string }>; + }>; + generatedAt: string; +} diff --git a/extensions/is-it-alive/src/types/statuspage.ts b/extensions/is-it-alive/src/types/statuspage.ts new file mode 100644 index 00000000000..183612fc3dd --- /dev/null +++ b/extensions/is-it-alive/src/types/statuspage.ts @@ -0,0 +1,39 @@ +import { StatusIndicator } from "@/types"; + +export interface StatuspageSummary { + page: { name: string; url: string }; + status: { indicator: StatusIndicator; description: string }; + components: Array<{ + id: string; + name: string; + status: string; + group?: boolean; + }>; + incidents?: Array<{ + id: string; + name: string; + status: string; + impact: string; + updated_at: string; + incident_updates?: Array<{ + body: string; + affected_components?: Array<{ code: string }>; + }>; + components?: Array<{ id: string }>; + }>; +} + +export interface StatuspageIncident { + id: string; + name: string; + status: string; + impact: string; + updated_at: string; + created_at: string; + resolved_at: string | null; + incident_updates?: Array<{ + body: string; + affected_components?: Array<{ code: string }>; + }>; + components?: Array<{ id: string }>; +} diff --git a/extensions/is-it-alive/tsconfig.json b/extensions/is-it-alive/tsconfig.json new file mode 100644 index 00000000000..1ec71e680fd --- /dev/null +++ b/extensions/is-it-alive/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "include": ["src/**/*", "raycast-env.d.ts"], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "lib": ["ES2023"], + "module": "commonjs", + "target": "ES2023", + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "jsx": "react-jsx", + "resolveJsonModule": true + } +}