From 72a76114f77a2776e22130893e0fcdd40d606ffd Mon Sep 17 00:00:00 2001 From: hey Date: Wed, 7 Oct 2026 17:15:00 -0400 Subject: [PATCH 1/6] Use abra container image add ssh manually, keeping slim node image --- Dockerfile | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index f1d2aba..104dee0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,15 +3,22 @@ RUN mkdir /backend COPY --parents ["cli", "internal", "go.mod", "go.sum", "app.go", "/backend/"] RUN go build -C /backend -o gobackend -FROM node:slim +FROM git.coopcloud.tech/toolshed/abra@sha256:38de102e0d4cf40558bd354edb46fa9ae2438221742e74366560c1cfa0b4ced7 AS abra + +FROM node:26-bookworm-slim +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + openssh-client \ + ca-certificates \ + git \ + && rm -rf /var/lib/apt/lists/* RUN mkdir /home/node/wizard COPY --from=0 /backend/gobackend /home/node/wizard/gobackend USER node -COPY ./command.sh / -RUN curl https://install.abra.coopcloud.tech | bash -ENV ABRA_BIN=/home/node/.local/bin/abra -# RUN $ABRA_BIN recipe fetch -a + +COPY --from=abra /abra /usr/local/bin/abra +COPY ./command.sh /command.sh COPY --parents web /home/node/wizard WORKDIR /home/node/wizard/web -- 2.54.0 From 3b192f91130158d21a67d505464233a5d676f1c2 Mon Sep 17 00:00:00 2001 From: hey Date: Wed, 7 Oct 2026 17:23:31 -0400 Subject: [PATCH 2/6] push new 'nightly' tag and update readme --- .gitea/workflows/build.yaml | 1 + .gitea/workflows/tag-build.yaml | 2 +- README.md | 30 ++++++++++-------------------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 7c605ab..91aa13a 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -42,3 +42,4 @@ jobs: platforms: linux/amd64 push: true tags: git.coopcloud.tech/toolshed/coop-cloud-webui:nightly-${{ env.COMMIT_SHORT_SHA }} + tags: git.coopcloud.tech/toolshed/coop-cloud-webui:nightly diff --git a/.gitea/workflows/tag-build.yaml b/.gitea/workflows/tag-build.yaml index 4192172..e287c94 100644 --- a/.gitea/workflows/tag-build.yaml +++ b/.gitea/workflows/tag-build.yaml @@ -50,4 +50,4 @@ jobs: file: ./Dockerfile platforms: linux/amd64 push: true - tags: git.coopcloud.tech/toolshed/coop-cloud-webui:${{ env.RELEASE_TAG }} + tags: git.coopcloud.tech/toolshed/coop-cloud-webui:${{ env.RELEASE_TAG }} \ No newline at end of file diff --git a/README.md b/README.md index 2f70c32..427af65 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,14 @@ A Go service that exposes RESTful API endpoints to manage Abra programmatically. Integrates with https://git.coopcloud.tech/toolshed/coop-cloud-front. ## Starting the service with Docker -Build the container: +Pull the container: ```bash -docker build -t coop-cloud-wizard:latest . +docker pull git.coopcloud.tech/toolshed/coop-cloud-webui:nightly ``` Run the container: ```bash -docker run -u 1000:1000 -p 5173:5173 -p 3000:3000 -v $HOME/.ssh:/home/node/.ssh -v $HOME/.abra:/home/node/.abra coop-cloud-wizard +docker run -u 1000:1000 -p 5173:5173 -p 3000:3000 -v $HOME/.ssh:/home/node/.ssh -v $HOME/.abra:/home/node/.abra coop-cloud-webui ``` ## Getting started with development @@ -20,29 +20,19 @@ docker run -u 1000:1000 -p 5173:5173 -p 3000:3000 -v $HOME/.ssh:/home/node/.ssh - Start this Go app `go run .` - Navigate to the React App (http://localhost:5173) -> [!WARNING] -> This is an extremely early prototype, only viewing/deploying apps is supported -> and may fail for your local machine - -## Notes: -`api/` is a deprecated path, currently has no function # Roadmap ## General Roadmap: -Currently refactored the backend to be much more Abra CLI reliant. This is ok but is not ideal from an engineering standpoint (probably). -First step is just to polish this backend & frontend, estimate 1 - 2 months to be in a fairly usable state. -Write test suite for the full stack combination. -Then we want to package this app into a dockerized form, easily to deploy and use on one's own server. -Slowly introduce full Abra CLI functionality into app. +TODO Repackaging ## Basic Abra Features | Feature | Status | Follow-up | | ----------- | ----------- | ----------- | | More options for app deploy (esp chaos) | TODO | | -| View and pull in recipes from catalogue | In Progress | | -| Deploy should show deployment status similar to Abra CLI | In Progress | | -| App screen should display services associated with app and their status | TODO | | -| Easily show service logs | In Progress | | -| Modify App config | TODO | | -| Manage updates and rollbacks | TODO | | +| View and pull in recipes from catalogue | Done | Refine Frontend | +| Deploy should show deployment status similar to Abra CLI | Done | Make more responsive and have page render more accurately | +| App screen should display services associated with app and their status | Done | | +| Easily show service logs | Done | More ways to process logs? | +| Modify App config | Done | | +| Manage updates and rollbacks | Done | | | Manage secrets and Abra commands | In Progress | | -- 2.54.0 From 8f3e491be5f35546b92d917d6a925577db1cd88a Mon Sep 17 00:00:00 2001 From: hey Date: Wed, 7 Oct 2026 17:30:27 -0400 Subject: [PATCH 3/6] one more readme change --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 427af65..90b0061 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,14 @@ Pull the container: ```bash docker pull git.coopcloud.tech/toolshed/coop-cloud-webui:nightly ``` - Run the container: ```bash docker run -u 1000:1000 -p 5173:5173 -p 3000:3000 -v $HOME/.ssh:/home/node/.ssh -v $HOME/.abra:/home/node/.abra coop-cloud-webui ``` - +Upstream container not working? You can build the container manually: +``` +docker build -t coop-cloud-webui:latest . +``` ## Getting started with development - Clone the front end application `git clone https://git.coopcloud.tech/toolshed/coop-cloud-front.git` - Checkout the `dev-nomock` branch `cd coop-cloud-front && git checkout dev-nomock` -- 2.54.0 From af31229c1178f24bf1ab62ea0ea67bfeaae08339 Mon Sep 17 00:00:00 2001 From: hey Date: Fri, 9 Oct 2026 11:44:15 -0400 Subject: [PATCH 4/6] removed web/ submodule --- .gitmodules | 3 --- Dockerfile | 3 +-- README.md | 2 +- web | 1 - 4 files changed, 2 insertions(+), 7 deletions(-) delete mode 160000 web diff --git a/.gitmodules b/.gitmodules index 818a087..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "web"] - path = web - url = https://git.coopcloud.tech/toolshed/coop-cloud-front.git diff --git a/Dockerfile b/Dockerfile index 104dee0..a3975fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,14 +13,13 @@ RUN apt-get update \ git \ && rm -rf /var/lib/apt/lists/* RUN mkdir /home/node/wizard +COPY --parents web /home/node/wizard COPY --from=0 /backend/gobackend /home/node/wizard/gobackend USER node - COPY --from=abra /abra /usr/local/bin/abra COPY ./command.sh /command.sh -COPY --parents web /home/node/wizard WORKDIR /home/node/wizard/web USER root RUN npm install . diff --git a/README.md b/README.md index 90b0061..d413b24 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Run the container: docker run -u 1000:1000 -p 5173:5173 -p 3000:3000 -v $HOME/.ssh:/home/node/.ssh -v $HOME/.abra:/home/node/.abra coop-cloud-webui ``` Upstream container not working? You can build the container manually: -``` +```bash docker build -t coop-cloud-webui:latest . ``` ## Getting started with development diff --git a/web b/web deleted file mode 160000 index e09aa9a..0000000 --- a/web +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e09aa9a1cade23adf20710625d7469aa5c4bbd87 -- 2.54.0 From 932e36cc9b3074d4c7039361e975560b202c590d Mon Sep 17 00:00:00 2001 From: hey Date: Fri, 9 Oct 2026 12:38:58 -0400 Subject: [PATCH 5/6] add gitignore for subtree --- web/.gitignore | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 web/.gitignore diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..6a1f366 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +pnpm-lock.yaml +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? -- 2.54.0 From e01d9df2d74831981ef3e81f4f9ebfc84462cdb0 Mon Sep 17 00:00:00 2001 From: hey Date: Fri, 9 Oct 2026 12:49:04 -0400 Subject: [PATCH 6/6] add full subtree corresponding to coop-cloud-front in web/ directory --- web/.env | 3 + web/.tool-versions | 2 + web/.vite/deps/_metadata.json | 8 + web/.vite/deps/package.json | 3 + web/README.md | 52 + web/eslint.config.js | 50 + web/index.html | 17 + web/package-lock.json | 5178 ++++++++++++++++++++ web/package.json | 49 + web/pnpm-workspace.yaml | 4 + web/public/favicon.ico | Bin 0 -> 48974 bytes web/public/fonts/Lora-Italic.woff2 | Bin 0 -> 77028 bytes web/public/fonts/manrope.bold.woff2 | Bin 0 -> 30008 bytes web/public/fonts/manrope.light.woff2 | Bin 0 -> 29720 bytes web/public/fonts/manrope.medium.woff2 | Bin 0 -> 29664 bytes web/src/App.tsx | 28 + web/src/assets/coopcloud_logo_grey.svg | 140 + web/src/assets/scss/_fonts.scss | 31 + web/src/assets/scss/_global.scss | 287 ++ web/src/assets/scss/_mixins.scss | 212 + web/src/assets/scss/_variables.scss | 76 + web/src/assets/scss/main.scss | 7 + web/src/components/Editor/Editor.tsx | 52 + web/src/components/Editor/Secrets.scss | 150 + web/src/components/Editor/Secrets.tsx | 208 + web/src/components/Header/Header.tsx | 32 + web/src/components/Header/_Header.scss | 83 + web/src/components/Loader/Loader.scss | 20 + web/src/components/Loader/Loader.tsx | 35 + web/src/components/Spinner/Spinner.scss | 43 + web/src/components/Spinner/Spinner.tsx | 9 + web/src/components/Terminal/Terminal.tsx | 83 + web/src/components/Terminal/_Terminal.scss | 204 + web/src/index.scss | 93 + web/src/main.tsx | 10 + web/src/routes/Apps/App.scss | 591 +++ web/src/routes/Apps/App.tsx | 760 +++ web/src/routes/Apps/Apps.scss | 167 + web/src/routes/Apps/Apps.tsx | 289 ++ web/src/routes/Dashboard/Dashboard.tsx | 136 + web/src/routes/Dashboard/_Dashboard.scss | 59 + web/src/routes/Recipes/Recipe.scss | 0 web/src/routes/Recipes/Recipe.tsx | 0 web/src/routes/Recipes/RecipeForm.scss | 159 + web/src/routes/Recipes/RecipeForm.tsx | 158 + web/src/routes/Recipes/Recipes.scss | 154 + web/src/routes/Recipes/Recipes.tsx | 179 + web/src/routes/Servers/Server.scss | 340 ++ web/src/routes/Servers/Server.tsx | 378 ++ web/src/routes/Servers/Servers.scss | 157 + web/src/routes/Servers/Servers.tsx | 259 + web/src/services/api.ts | 287 ++ web/src/services/mock-apps.json | 593 +++ web/src/services/mock-catalogue.json | 1726 +++++++ web/src/services/mock-logs.json | 326 ++ web/src/services/mock-servers.json | 30 + web/src/services/mockApi.ts | 145 + web/src/types/index.ts | 126 + web/tsconfig.app.json | 28 + web/tsconfig.json | 7 + web/tsconfig.node.json | 26 + web/vite.config.ts | 6 + 62 files changed, 14255 insertions(+) create mode 100644 web/.env create mode 100644 web/.tool-versions create mode 100644 web/.vite/deps/_metadata.json create mode 100644 web/.vite/deps/package.json create mode 100644 web/README.md create mode 100644 web/eslint.config.js create mode 100644 web/index.html create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/pnpm-workspace.yaml create mode 100644 web/public/favicon.ico create mode 100644 web/public/fonts/Lora-Italic.woff2 create mode 100644 web/public/fonts/manrope.bold.woff2 create mode 100644 web/public/fonts/manrope.light.woff2 create mode 100644 web/public/fonts/manrope.medium.woff2 create mode 100644 web/src/App.tsx create mode 100644 web/src/assets/coopcloud_logo_grey.svg create mode 100644 web/src/assets/scss/_fonts.scss create mode 100644 web/src/assets/scss/_global.scss create mode 100644 web/src/assets/scss/_mixins.scss create mode 100644 web/src/assets/scss/_variables.scss create mode 100644 web/src/assets/scss/main.scss create mode 100644 web/src/components/Editor/Editor.tsx create mode 100644 web/src/components/Editor/Secrets.scss create mode 100644 web/src/components/Editor/Secrets.tsx create mode 100644 web/src/components/Header/Header.tsx create mode 100644 web/src/components/Header/_Header.scss create mode 100644 web/src/components/Loader/Loader.scss create mode 100644 web/src/components/Loader/Loader.tsx create mode 100644 web/src/components/Spinner/Spinner.scss create mode 100644 web/src/components/Spinner/Spinner.tsx create mode 100644 web/src/components/Terminal/Terminal.tsx create mode 100644 web/src/components/Terminal/_Terminal.scss create mode 100644 web/src/index.scss create mode 100644 web/src/main.tsx create mode 100644 web/src/routes/Apps/App.scss create mode 100644 web/src/routes/Apps/App.tsx create mode 100644 web/src/routes/Apps/Apps.scss create mode 100644 web/src/routes/Apps/Apps.tsx create mode 100644 web/src/routes/Dashboard/Dashboard.tsx create mode 100644 web/src/routes/Dashboard/_Dashboard.scss create mode 100644 web/src/routes/Recipes/Recipe.scss create mode 100644 web/src/routes/Recipes/Recipe.tsx create mode 100644 web/src/routes/Recipes/RecipeForm.scss create mode 100644 web/src/routes/Recipes/RecipeForm.tsx create mode 100644 web/src/routes/Recipes/Recipes.scss create mode 100644 web/src/routes/Recipes/Recipes.tsx create mode 100644 web/src/routes/Servers/Server.scss create mode 100644 web/src/routes/Servers/Server.tsx create mode 100644 web/src/routes/Servers/Servers.scss create mode 100644 web/src/routes/Servers/Servers.tsx create mode 100644 web/src/services/api.ts create mode 100644 web/src/services/mock-apps.json create mode 100644 web/src/services/mock-catalogue.json create mode 100644 web/src/services/mock-logs.json create mode 100644 web/src/services/mock-servers.json create mode 100644 web/src/services/mockApi.ts create mode 100644 web/src/types/index.ts create mode 100644 web/tsconfig.app.json create mode 100644 web/tsconfig.json create mode 100644 web/tsconfig.node.json create mode 100644 web/vite.config.ts diff --git a/web/.env b/web/.env new file mode 100644 index 0000000..a53bee2 --- /dev/null +++ b/web/.env @@ -0,0 +1,3 @@ +VITE_API_URL=http://localhost:3000/api +VITE_WS_URL=ws://localhost:3000 +VITE_MOCK_AUTH=true \ No newline at end of file diff --git a/web/.tool-versions b/web/.tool-versions new file mode 100644 index 0000000..7cada80 --- /dev/null +++ b/web/.tool-versions @@ -0,0 +1,2 @@ +pnpm 10.20.0 +nodejs 22.12.0 \ No newline at end of file diff --git a/web/.vite/deps/_metadata.json b/web/.vite/deps/_metadata.json new file mode 100644 index 0000000..988ccbc --- /dev/null +++ b/web/.vite/deps/_metadata.json @@ -0,0 +1,8 @@ +{ + "hash": "97e400e8", + "configHash": "539cfeba", + "lockfileHash": "e3b0c442", + "browserHash": "b743bb36", + "optimized": {}, + "chunks": {} +} \ No newline at end of file diff --git a/web/.vite/deps/package.json b/web/.vite/deps/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/web/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..de431f2 --- /dev/null +++ b/web/README.md @@ -0,0 +1,52 @@ +# Coop Cloud Front + +Frontend for Coop Cloud — a web UI wrapper around the `abra` CLI for +managing VPSs, containers and application deployments. + +This repository contains a Vite + React + TypeScript app styled +with SCSS. + +## Quick start + +- Install dependencies (pnpm is recommended): + +pnpm install +pnpm dev + +``` + +The app uses Vite; start the dev server with `pnpm dev`. + +## Environment + +- The app reads runtime flags from Vite env variables. The default build uses mocked api data. +-To deploy on a real API without mocking, run: +pnpm start:prod + +Modify `.env` file at the project root if you need to override values for +development (see Vite docs for env var conventions). + +## Scripts + +- `pnpm dev` — start dev server +- `pnpm build` — run TypeScript and build production assets +- `pnpm preview` — preview production build locally +- `pnpm lint` — run ESLint +- `pnpm lint:scss` — run Stylelint +- `pnpm format` — format with Prettier + +## Notes about local development + +- The repo contains mock JSON and a `mockApi` service to develop UI without + a backend. Toggle mock mode with `VITE_MOCK_AUTH=true`. +- Routes are client-side using `react-router-dom`. + +## Next steps / suggested work items + +- Add form validation and better UX for `RecipeForm`. +- Improve accessibility. +- Add unit tests. +- Improve responsive layouts and card grid behaviour on narrow screens. +- Add deployment docs and production environment variables. + +--- \ No newline at end of file diff --git a/web/eslint.config.js b/web/eslint.config.js new file mode 100644 index 0000000..ad90ba5 --- /dev/null +++ b/web/eslint.config.js @@ -0,0 +1,50 @@ +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + + // TypeScript specific + '@typescript-eslint/no-unused-vars': ['warn', { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_' + }], + '@typescript-eslint/no-explicit-any': 'warn', + + // Import organization + 'sort-imports': ['warn', { + ignoreCase: true, + ignoreDeclarationSort: true, + }], + + // React best practices + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + + // Code quality + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'prefer-const': 'warn', + 'no-var': 'error', + }, + }, +); \ No newline at end of file diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..85b717b --- /dev/null +++ b/web/index.html @@ -0,0 +1,17 @@ + + + + + + + coopcloud-frontend + + + + + + +
+ + + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..66495db --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,5178 @@ +{ + "name": "coopcloud-front", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "coopcloud-front", + "version": "0.0.0", + "dependencies": { + "lucide-react": "^1.47.0", + "niceware": "^4.0.0", + "niceware-ts": "^0.0.5", + "prismjs": "^1.30.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "react-icons": "^5.6.0", + "react-router-dom": "^7.9.5", + "react-simple-code-editor": "^0.14.1" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@types/node": "^24.6.0", + "@types/react": "^19.1.16", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.4", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.22", + "globals": "^16.4.0", + "prettier": "^3.6.2", + "sass": "^1.93.3", + "stylelint": "^16.25.0", + "stylelint-config-prettier-scss": "^1.0.0", + "stylelint-config-standard-scss": "^16.0.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0", + "vite": "^7.1.7" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cacheable/memory": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.8.tgz", + "integrity": "sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.4.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/memory/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.4.0.tgz", + "integrity": "sha512-PeMMsqjVq+bF0WBsxFBxr/WozBJiZKY0rUojuaCoIaKnEl3Ju1wfEwS+SV1DU/cSe8fqHIPiYJFif8T3MVt4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.0", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.0.tgz", + "integrity": "sha512-H4tuz2nhWgNKLt1inYpoVCfbJbMwX/lQKp3g69rrrIMIYlFD9+zTykOKhNR8uGrAmbS/kT9n6hTFkmDkxLgeTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0" + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.2.1.tgz", + "integrity": "sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/JounQin" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "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/eslint-utils/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-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-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/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/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.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.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/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "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": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", + "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/type-utils": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.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.57.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.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.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", + "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", + "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.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", + "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.57.0", + "@typescript-eslint/types": "^8.57.0", + "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.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", + "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.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/tsconfig-utils": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", + "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", + "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.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", + "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.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.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", + "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", + "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.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", + "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.57.0", + "@typescript-eslint/tsconfig-utils": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.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.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", + "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.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.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", + "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.57.0", + "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/@vitejs/plugin-react": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", + "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==", + "license": "CC0-1.0" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/cacheable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.3.tgz", + "integrity": "sha512-iffYMX4zxKp54evOH27fm92hs+DeC1DhXmNVN8Tr94M/iZIV42dqTHSR2Ik4TOSPyOAwKr7Yu3rN9ALoLkbWyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.0.8", + "@cacheable/utils": "^2.4.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.6.0" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "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/caniuse-lite": { + "version": "1.0.30001777", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", + "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true, + "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/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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/css-functions-list": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", + "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.307", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", + "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", + "dev": true, + "license": "ISC" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "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-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "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": "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/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/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "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/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "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/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "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.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.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/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/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/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hashery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.0.tgz", + "integrity": "sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/immutable": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "dev": true, + "license": "MIT" + }, + "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/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "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/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/known-css-properties": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.37.0.tgz", + "integrity": "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==", + "dev": true, + "license": "MIT" + }, + "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/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.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/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.54.0.tgz", + "integrity": "sha512-kVmnVCBoLztTGe3S76xu8utwxijRLOinknNq9e8btZ0R2fm6WCgxY+weaLiA50odo/yi+apm/4B2A/LsFZhTrw==", + "license": "ISC", + "peerDependencies": { + "@types/react": "*", + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/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/niceware": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/niceware/-/niceware-4.0.0.tgz", + "integrity": "sha512-7uFcxHr5zwNZO4NEMwxK0N+GbqVMGp2YLZ8x/0dx/nxiT0qCt1zL3hZfqawQoq4XiP2OWa665Xid0g/LyoMfQA==", + "license": "MIT", + "dependencies": { + "binary-search": "^1.3.6", + "randombytes": "^2.0.6" + } + }, + "node_modules/niceware-ts": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/niceware-ts/-/niceware-ts-0.0.5.tgz", + "integrity": "sha512-50NqCnl/kT8wABYC8OnXPQxZ4iMGlU0Cf0lbqwkz1rqKpugDU2y/SruWOainL3Pr0ijniSjRAEZlJhQ2jqfWMg==", + "license": "MIT License", + "dependencies": { + "typescript": "^4.1.0" + } + }, + "node_modules/niceware-ts/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/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/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/qified": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.6.0.tgz", + "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.14.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-icons": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", + "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", + "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", + "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-simple-code-editor": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.14.1.tgz", + "integrity": "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.98.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz", + "integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-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/stylelint": { + "version": "16.26.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.26.1.tgz", + "integrity": "sha512-v20V59/crfc8sVTAtge0mdafI3AdnzQ2KsWe6v523L4OA1bJO02S7MO2oyXDCS6iWb9ckIPnqAFVItqSBQr7jw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-syntax-patches-for-csstree": "^1.0.19", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3", + "@csstools/selector-specificity": "^5.0.0", + "@dual-bundle/import-meta-resolve": "^4.2.1", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.3", + "css-tree": "^3.1.0", + "debug": "^4.4.3", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^11.1.1", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^7.0.5", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.37.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.6", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.0", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "supports-hyperlinks": "^3.2.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint-config-prettier-scss": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier-scss/-/stylelint-config-prettier-scss-1.0.0.tgz", + "integrity": "sha512-Gr2qLiyvJGKeDk0E/+awNTrZB/UtNVPLqCDOr07na/sLekZwm26Br6yYIeBYz3ulsEcQgs5j+2IIMXCC+wsaQA==", + "dev": true, + "license": "MIT", + "bin": { + "stylelint-config-prettier-scss": "bin/check.js", + "stylelint-config-prettier-scss-check": "bin/check.js" + }, + "engines": { + "node": "14.* || 16.* || >= 18" + }, + "peerDependencies": { + "stylelint": ">=15.0.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-17.0.0.tgz", + "integrity": "sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.23.0" + } + }, + "node_modules/stylelint-config-recommended-scss": { + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-16.0.2.tgz", + "integrity": "sha512-aUTHhPPWCvFyWaxtckJlCPaXTDFsp4pKO8evXNCsW9OwsaUWyMd6jvcUhSmfGWPrTddvzNqK4rS/UuSLcbVGdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-scss": "^4.0.9", + "stylelint-config-recommended": "^17.0.0", + "stylelint-scss": "^6.12.1" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "postcss": "^8.3.3", + "stylelint": "^16.24.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + } + } + }, + "node_modules/stylelint-config-standard": { + "version": "39.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-39.0.1.tgz", + "integrity": "sha512-b7Fja59EYHRNOTa3aXiuWnhUWXFU2Nfg6h61bLfAb5GS5fX3LMUD0U5t4S8N/4tpHQg3Acs2UVPR9jy2l1g/3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "stylelint-config-recommended": "^17.0.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.23.0" + } + }, + "node_modules/stylelint-config-standard-scss": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-16.0.0.tgz", + "integrity": "sha512-/FHECLUu+med/e6OaPFpprG86ShC4SYT7Tzb2PTVdDjJsehhFBOioSlWqYFqJxmGPIwO3AMBxNo+kY3dxrbczA==", + "dev": true, + "license": "MIT", + "dependencies": { + "stylelint-config-recommended-scss": "^16.0.1", + "stylelint-config-standard": "^39.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "postcss": "^8.3.3", + "stylelint": "^16.23.1" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + } + } + }, + "node_modules/stylelint-scss": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-6.14.0.tgz", + "integrity": "sha512-ZKmHMZolxeuYsnB+PCYrTpFce0/QWX9i9gh0hPXzp73WjuIMqUpzdQaBCrKoLWh6XtCFSaNDErkMPqdjy1/8aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.1", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.37.0", + "mdn-data": "^2.25.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-selector-parser": "^7.1.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.8.2" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.2.tgz", + "integrity": "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.20" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "6.1.20", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.20.tgz", + "integrity": "sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^2.3.2", + "flatted": "^3.3.3", + "hookified": "^1.15.0" + } + }, + "node_modules/stylelint/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/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "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/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.57.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", + "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.57.0", + "@typescript-eslint/parser": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.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.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..bf3e2ef --- /dev/null +++ b/web/package.json @@ -0,0 +1,49 @@ +{ + "name": "coopcloud-front", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "vite", + "dev": "vite", + "start:prod": "VITE_MOCK_AUTH=false pnpm start", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint:fix": "eslint . --ext ts,tsx --fix", + "lint:scss": "stylelint '**/*.scss'", + "lint:scss:fix": "stylelint '**/*.scss' --fix", + "format": "prettier --write 'src/**/*.{ts,tsx,scss,css,json}'", + "format:check": "prettier --check 'src/**/*.{ts,tsx,scss,css,json}'" + }, + "dependencies": { + "lucide-react": "^1.47.0", + "niceware": "^4.0.0", + "niceware-ts": "^0.0.5", + "prismjs": "^1.30.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "react-icons": "^5.6.0", + "react-router-dom": "^7.9.5", + "react-simple-code-editor": "^0.14.1" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@types/node": "^24.6.0", + "@types/react": "^19.1.16", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.4", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.22", + "globals": "^16.4.0", + "prettier": "^3.6.2", + "sass": "^1.93.3", + "stylelint": "^16.25.0", + "stylelint-config-prettier-scss": "^1.0.0", + "stylelint-config-standard-scss": "^16.0.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0", + "vite": "^7.1.7" + } +} diff --git a/web/pnpm-workspace.yaml b/web/pnpm-workspace.yaml new file mode 100644 index 0000000..6c7d358 --- /dev/null +++ b/web/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +allowBuilds: + '@parcel/watcher': true + esbuild: true + inotify: false diff --git a/web/public/favicon.ico b/web/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6a2cec6eac0dad801b3d3f974c60439d2d9da313 GIT binary patch literal 48974 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
%{tw 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 literal 0 HcmV?d00001 diff --git a/web/public/fonts/Lora-Italic.woff2 b/web/public/fonts/Lora-Italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..36bea0791d89665a4352d063d782a16d14531a98 GIT binary patch literal 77028 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

*M8+IVc-x^u%qx=6)u-4r&cn?b}Yg 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 T=P#oUZ`EcT^uGLmclvP?2}0uv literal 0 HcmV?d00001 diff --git a/web/public/fonts/manrope.light.woff2 b/web/public/fonts/manrope.light.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0efa5d823251b9a348137aec51f533ed674c3680 GIT binary patch literal 29720 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 literal 0 HcmV?d00001 diff --git a/web/public/fonts/manrope.medium.woff2 b/web/public/fonts/manrope.medium.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..19b8b2386bbd0413bb5d4d47d94db4aa071863d1 GIT binary patch literal 29664 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 literal 0 HcmV?d00001 diff --git a/web/src/App.tsx b/web/src/App.tsx new file mode 100644 index 0000000..6d37791 --- /dev/null +++ b/web/src/App.tsx @@ -0,0 +1,28 @@ +import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; +import { Dashboard } from './routes/Dashboard/Dashboard'; +import { Apps } from './routes/Apps/Apps'; +import { AppDetail } from './routes/Apps/App'; +import { Servers } from './routes/Servers/Servers'; +import { Server } from './routes/Servers/Server'; +import { Recipes } from './routes/Recipes/Recipes'; + +function App() { + return ( + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + {/* 404 catch-all */} + } /> + + + ); +} + +export default App; diff --git a/web/src/assets/coopcloud_logo_grey.svg b/web/src/assets/coopcloud_logo_grey.svg new file mode 100644 index 0000000..b55fcb5 --- /dev/null +++ b/web/src/assets/coopcloud_logo_grey.svg @@ -0,0 +1,140 @@ + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/src/assets/scss/_fonts.scss b/web/src/assets/scss/_fonts.scss new file mode 100644 index 0000000..0d5ca7e --- /dev/null +++ b/web/src/assets/scss/_fonts.scss @@ -0,0 +1,31 @@ +@font-face { + font-family: 'Lora'; + src: url('/fonts/Lora-Italic.woff2') format('woff2'); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: 'Manrope'; + src: url('/fonts/manrope.light.woff2') format('woff2'); + font-weight: 300; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Manrope'; + src: url('/fonts/manrope.medium.woff2') format('woff2'); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Manrope'; + src: url('/fonts/manrope.bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} diff --git a/web/src/assets/scss/_global.scss b/web/src/assets/scss/_global.scss new file mode 100644 index 0000000..612f936 --- /dev/null +++ b/web/src/assets/scss/_global.scss @@ -0,0 +1,287 @@ +@use './variables' as *; +@use './mixins' as *; + + +body { + margin: 0; + padding: 0; + width: 100%; + min-height: 100vh; +} + +#root { + width: 100%; + min-height: 100vh; +} + +// global page layout styles +.page-wrapper { + min-height: 100vh; + background-color: $bg-secondary; +} + +.page-content { + max-width: 1600px; + margin: 0 auto; + padding: $spacing-2xl $spacing-xl; + + @media (max-width: 768px) { + padding: $spacing-xl $spacing-md; + } +} + +.page-header { + margin-bottom: $spacing-2xl; + + h1 { + font-size: $font-size-3xl; + margin: 0 0 $spacing-sm; + color: $text-primary; + } + + .subtitle { + font-size: $font-size-lg; + color: $text-secondary; + margin: 0; + } +} + +// global state styles +.loading, +.error { + text-align: center; + padding: $spacing-3xl; + font-size: $font-size-lg; +} + +.loading { + color: $text-secondary; +} + +.error { + color: $error; +} + +// Stats grid and cards +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: $spacing-lg; + margin-bottom: $spacing-2xl; +} + +.stat-card { + @include card; + display: flex; + align-items: center; + gap: $spacing-lg; + padding: $spacing-xl; + transition: transform $transition-base, box-shadow $transition-base; + + &:hover { + transform: translateY(-2px); + box-shadow: $shadow-lg; + } + + // Modifier classes for colored borders + &.upgrade { + border-left: 4px solid $primary-light; + cursor: none; + } + + &.chaos { + border-left: 4px solid $primary-dark; + } + + &.primary { + border-left: 4px solid $primary; + } + + .stat-icon { + font-size: 2rem; + } + + .stat-info { + flex: 1; + + .stat-number { + font-size: $font-size-3xl; + font-weight: $font-weight-bold; + color: $text-primary; + margin: 0; + line-height: 1; + } + + .stat-label { + font-size: $font-size-sm; + color: $text-secondary; + margin: $spacing-xs 0 0; + text-transform: uppercase; + letter-spacing: 0.5px; + } + } +} + +// Filters component +.filters { + @include card; + display: flex; + gap: $spacing-md; + flex-wrap: wrap; + margin-bottom: $spacing-xl; + + @media (max-width: 768px) { + flex-direction: column; + } + + .search-input { + background-color: $bg-primary; + flex: 1; + min-width: 250px; + padding: $spacing-sm $spacing-md; + border: 2px solid $border-color; + border-radius: $radius-md; + font-size: $font-size-base; + transition: border-color $transition-base; + color: $text-primary; + + &::placeholder { + color: $text-muted; + } + + &:focus { + outline: none; + border-color: $primary; + } + } + + select { + padding: $spacing-sm $spacing-md; + border: 2px solid $border-color; + border-radius: $radius-md; + font-size: $font-size-base; + background-color: $bg-primary; + color: $text-primary; + cursor: pointer; + transition: border-color $transition-base; + + &:focus { + outline: none; + border-color: $primary; + } + } + + .checkbox-filter { + display: flex; + align-items: center; + gap: $spacing-sm; + cursor: pointer; + user-select: none; + + input[type="checkbox"] { + cursor: pointer; + width: 18px; + height: 18px; + } + + span { + font-size: $font-size-sm; + color: $text-primary; + } + } +} + +// Status badges +.status-badge { + display: inline-block; + padding: $spacing-xs $spacing-sm; + border-radius: $radius-full; + font-size: $font-size-xs; + font-weight: $font-weight-semibold; + text-transform: capitalize; + + &.status-deployed { + background-color: rgba($success, 0.1); + color: $success; + } + + &.status-stopped { + background-color: rgba($text-muted, 0.1); + color: $text-muted; + } + + &.status-error { + background-color: rgba($error, 0.1); + color: $error; + } +} + +// global badge styles +.recipe-badge, +.server-badge { + display: inline-block; + padding: $spacing-xs $spacing-sm; + border-radius: $radius-sm; + font-size: $font-size-sm; + font-weight: $font-weight-medium; +} + +.recipe-badge { + background-color: rgba($info, 0.1); + color: $info; +} + +.server-badge { + background-color: rgba($text-secondary, 0.1); + color: $text-secondary; +} + +// Ensure stat-chip shows primary-dark outline on hover and when active +.stat-chip { + &:hover:not(:disabled), + &.active { + border-color: $primary-dark; + box-shadow: 0 0 0 3px rgba($primary-dark, 0.08); + } +} +.filter-chip { + cursor: default; +} + +// Results count +.results-count { + text-align: center; + color: $text-secondary; + font-size: $font-size-sm; + padding: $spacing-md; +} + +// No results message +.no-results { + @include card; + text-align: center; + padding: $spacing-3xl; + color: $text-secondary; + grid-column: 1 / -1; +} + +// Navigation link button (for clickable cards) +.nav-link { + all: unset; + cursor: pointer; + width: 100%; + display: block; + + &:focus-visible { + outline: 2px solid $primary; + outline-offset: 2px; + border-radius: $radius-md; + } +} + +.bland-button { + background: none; + border: none; + padding: 0; + margin: 0; +} diff --git a/web/src/assets/scss/_mixins.scss b/web/src/assets/scss/_mixins.scss new file mode 100644 index 0000000..2653d6a --- /dev/null +++ b/web/src/assets/scss/_mixins.scss @@ -0,0 +1,212 @@ +@use 'variables' as *; + +// Flexbox center +@mixin flex-center { + display: flex; + justify-content: center; + align-items: center; +} + +// Responsive breakpoints +@mixin respond-to($breakpoint) { + @if $breakpoint == 'sm' { + @media (min-width: $breakpoint-sm) { @content; } + } @else if $breakpoint == 'md' { + @media (min-width: $breakpoint-md) { @content; } + } @else if $breakpoint == 'lg' { + @media (min-width: $breakpoint-lg) { @content; } + } @else if $breakpoint == 'xl' { + @media (min-width: $breakpoint-xl) { @content; } + } +} + +// Card style +@mixin card { + background: $bg-primary; + box-shadow: $shadow-md; + padding: $spacing-xl; +} + +// Ensure cards occupy consistent vertical space and can stretch horizontally +@mixin card-dimensions($min-width: 320px, $min-height: 180px) { + min-width: $min-width; + min-height: $min-height; + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +/// Standard vertical stack for card-style list rows (dashboard recent apps, server detail apps, etc.) +@mixin card-list-vertical($gap: $spacing-md) { + display: flex; + flex-direction: column; + gap: $gap; +} + +/// Hover lift used by dashboard list cards, server grid cards, and similar surfaces +@mixin card-hover-lift($translate-y: -2px, $hover-shadow: $shadow-lg) { + transition: transform $transition-base, box-shadow $transition-base; + + &:hover { + transform: translateY($translate-y); + box-shadow: $hover-shadow; + } +} + +/// Card shell with horizontal scroll (e.g. data tables) +@mixin scrollable-card { + @include card; + overflow-x: auto; +} + +/// Header block inside a card: title area with a bottom rule (server cards, etc.) +@mixin card-header-rule { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: $spacing-lg; + padding-bottom: $spacing-md; + border-bottom: 2px solid $bg-secondary; +} + +/// Primary title + muted monospace/meta line (server cards, resource headers) +@mixin card-title-stack( + $title-size: $font-size-xl, + $title-weight: $font-weight-bold, + $meta-size: $font-size-sm +) { + h3 { + margin: 0 0 $spacing-xs; + font-size: $title-size; + color: $text-primary; + font-weight: $title-weight; + } + + .server-host { + font-size: $meta-size; + color: $text-muted; + font-family: monospace; + } +} + +/// Label + value row inside a card body (server stats, etc.) +@mixin card-stat-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: $spacing-sm 0; + border-bottom: 1px solid $bg-secondary; + + &:last-child { + border-bottom: none; + } +} + +/// Full-width edge bleed for highlighted rows inside padded cards (matches card horizontal padding) +@mixin card-row-highlight-bleed($clear-bottom-border: true) { + padding: $spacing-sm $spacing-md; + margin: $spacing-sm (-$spacing-xl); + padding-left: calc($spacing-xl + $spacing-md); + padding-right: calc($spacing-xl + $spacing-md); + + @if $clear-bottom-border { + border-bottom: none; + } +} + +// Button base +@mixin button-base { + padding: $spacing-sm $spacing-lg; + border: none; + border-radius: $radius-md; + font-weight: $font-weight-semibold; + cursor: pointer; + transition: all $transition-base; + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } +} + +// Gradient background +@mixin gradient-primary { + background: linear-gradient(135deg, $primary 0%, $primary-light 100%); +} + +// Truncate text +@mixin truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// Status badge +@mixin status-badge($color) { + padding: $spacing-xs $spacing-md; + border-radius: $radius-full; + font-size: $font-size-sm; + font-weight: $font-weight-semibold; + background-color: rgba($color, 0.1); + // color: darken($color, 20%); +} + +// Compact chip used for stat chips and small interactive chips +@mixin chip( + $bg: white, + $border-color: $border-color, + $radius: $radius-md, + $padding: $spacing-md $spacing-lg, + $gap: $spacing-md, + $hover-translate: -2px, + $hover-shadow: $shadow-sm, + $font-size: $font-size-base +) { + display: flex; + align-items: center; + gap: $gap; + padding: $padding; + background: $bg; + border: 2px solid $border-color; + border-radius: $radius; + cursor: pointer; + transition: all $transition-base; + font-size: $font-size; + + &:hover:not(:disabled) { + border-color: $primary; + transform: translateY($hover-translate); + box-shadow: $hover-shadow; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +// Reusable action button styles; accepts border width and radius +@mixin action-btn($border-width: 1px, $radius: $radius-sm, $padding: $spacing-xs $spacing-md, $font-size: $font-size-sm) { + background: none; + border: $border-width solid $border-color; + padding: $padding; + border-radius: $radius; + cursor: pointer; + font-size: $font-size; + color: $text-primary; + font-weight: $font-weight-medium; + transition: all $transition-base; + + &:hover { + background-color: rgba($primary, 0.05); + color: $text-primary; + transform: translateY(-1px); + border-color: $primary; + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } +} \ No newline at end of file diff --git a/web/src/assets/scss/_variables.scss b/web/src/assets/scss/_variables.scss new file mode 100644 index 0000000..a61020f --- /dev/null +++ b/web/src/assets/scss/_variables.scss @@ -0,0 +1,76 @@ +// Colors +$primary: #EFEFEF; +$primary-dark: #6A9CFF; +$primary-light: #ff4f88; +$secondary: #363636; + +$success: #10b981; +$warning: #f59e0b; +$error: #ef4444; +$info: #3b82f6; + +$text-primary: #363636; +$text-secondary: #4a4a4a +; +$text-muted: #999; + +$bg-primary: #ffffff; +$bg-secondary: #f5f5f5; +$bg-tertiary: #e0e0e0; + +$border-color: #e0e0e0; + +// Spacing +$spacing-2xs: 0.1rem; +$spacing-xs: 0.25rem; +$spacing-sm: 0.5rem; +$spacing-md: 1rem; +$spacing-lg: 1.5rem; +$spacing-xl: 2rem; +$spacing-2xl: 3rem; +$spacing-3xl: 4rem; + +// Typography + +$font-family-body: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', +'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', +'Helvetica Neue', sans-serif; + +$font-family-heading: 'Lora', serif; + +$font-size-xs: 0.75rem; +$font-size-sm: 0.875rem; +$font-size-base: 1rem; +$font-size-lg: 1.125rem; +$font-size-xl: 1.25rem; +$font-size-2xl: 1.5rem; +$font-size-3xl: 2rem; + +$font-weight-normal: 400; +$font-weight-medium: 500; +$font-weight-semibold: 600; +$font-weight-bold: 700; + +// Border radius +$radius-sm: 4px; +$radius-md: 6px; +$radius-lg: 8px; +$radius-xl: 12px; +$radius-full: 9999px; + +// Shadows +$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); +$shadow-md: 0 2px 4px rgba(0, 0, 0, 0.1); +$shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.15); +$shadow-xl: 0 8px 16px rgba(0, 0, 0, 0.2); + +// Transitions +$transition-fast: 0.15s ease; +$transition-base: 0.2s ease; +$transition-slow: 0.3s ease; + +// Breakpoints +$breakpoint-sm: 640px; +$breakpoint-md: 768px; +$breakpoint-lg: 1024px; +$breakpoint-xl: 1280px; \ No newline at end of file diff --git a/web/src/assets/scss/main.scss b/web/src/assets/scss/main.scss new file mode 100644 index 0000000..28c39d1 --- /dev/null +++ b/web/src/assets/scss/main.scss @@ -0,0 +1,7 @@ +// 1. Configuration & helpers (no CSS output) +@use 'variables'; +@use 'mixins'; +@use 'fonts'; + +// 2. Global base styles +@use 'global'; diff --git a/web/src/components/Editor/Editor.tsx b/web/src/components/Editor/Editor.tsx new file mode 100644 index 0000000..8e43ded --- /dev/null +++ b/web/src/components/Editor/Editor.tsx @@ -0,0 +1,52 @@ +import React, { useEffect, useState, useRef } from "react"; +import { apiService } from '../../services/api'; +import Editor from "react-simple-code-editor"; +import Prism from "prismjs"; + +// import a theme + language +import "prismjs/themes/prism.css"; +import "prismjs/components/prism-javascript"; + +interface EditorProps { + appName: string; +} + +export const EditorPage: React.FC = ({ appName }) => { + const [code, setCode] = useState(""); + useEffect(() => { + document.body.classList.add("modal-open"); + + return () => { + document.body.classList.remove("modal-open"); + }; + }, []); + useEffect(() => { + const fetchCode = async () => { + const config = await apiService.getConfig(appName); + setCode(config); + } + fetchCode(); + }, []); + + const saveFile = async () => { + await apiService.writeConfig(appName, code) + }; + + return ( +

+ setCode(code)} + highlight={code => Prism.highlight(code, Prism.languages.javascript, "javascript")} + padding={10} + style={{ + fontFamily: '"Fira code", "Fira Mono", monospace', + fontSize: 14, + border: "1px solid #ccc", + minHeight: "300px", + }} + /> + +
+ ); +}; \ No newline at end of file diff --git a/web/src/components/Editor/Secrets.scss b/web/src/components/Editor/Secrets.scss new file mode 100644 index 0000000..1d194f9 --- /dev/null +++ b/web/src/components/Editor/Secrets.scss @@ -0,0 +1,150 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; +@use "sass:color"; +.secrets { + color: $text-primary +} +.status { + white-space: nowrap; + min-width: 350px; + .manage-secret { + display: flex; + align-items: center; + gap: $spacing-sm; + &.created { + color: $success; + } + &.not-created { + color: $error; + } + } +} + +.gen-btn { + @include action-btn(1px, $radius-md, $spacing-xs $spacing-md, $font-size-sm); + white-space: nowrap; + &.auto { + + padding: $spacing-xs; + border: 1px solid #d0d7de; + border-radius: 6px; + + background: #fff; + cursor: pointer; + + transition: background-color 0.15s ease; + + &:hover:not(:disabled) { + background: #f3f4f6; + } + } + &.create { + margin-left: auto; + display: flex; + gap: 8px; + background: $success; + color: white; + border-color: $success; + + &:hover:not(:disabled) { + background: color.adjust($success, $lightness: -10%); + } + } + &.modify { + margin-left: auto; + display: flex; + gap: 8px; + background: $warning; + color: white; + border-color: $warning; + + &:hover:not(:disabled) { + background: color.adjust($warning, $lightness: -10%); + } + } + &.cancel { + background: $error; + color: white; + border-color: $error; + + &:hover:not(:disabled) { + background: color.adjust($error, $lightness: -10%); + } + } + +} +.secret-input-wrapper { + position: relative; + display: flex; + align-items: center; + width: 100%; + max-width: 420px; +} +.secret-input { + flex: 1; + min-width: 350px; + padding: $spacing-xs; + padding-right: calc( + 16px + + 16px + + 6px + + 16px + ); + font-size: $font-size-sm; + + border: 1px solid #d0d7de; + border-radius: $radius-md; + + background: #fff; + color: #24292f; + + transition: + border-color 0.15s ease, + box-shadow 0.15s ease; +} + +.secret-input::placeholder { + color: #8b949e; +} + +.secret-input:focus { + outline: none; + border-color: #2f81f7; + box-shadow: 0 0 0 3px rgba(47, 129, 247, 0.15); +} +.right-icons { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + + display: flex; + align-items: center; + gap: 6px; + + z-index: 1; +} + +.right-icons button { + all: unset; + display: flex; + align-items: center; + justify-content: center; + + width: 16px; + height: 16px; + + border: none; + background: transparent; + cursor: pointer; + + color: #6b7280; + border-radius: 4px; + + transition: background-color 0.15s ease; +} + +.right-icons button:hover { + color: #37393b; +} \ No newline at end of file diff --git a/web/src/components/Editor/Secrets.tsx b/web/src/components/Editor/Secrets.tsx new file mode 100644 index 0000000..3211ef4 --- /dev/null +++ b/web/src/components/Editor/Secrets.tsx @@ -0,0 +1,208 @@ +import React, { useEffect, useState, useRef } from "react"; +import { apiService } from '../../services/api'; +import type { AbraAppSecret } from '../../types' +import './Secrets.scss'; +import {generatePassphrase} from "niceware-ts"; +import { FaCopy, FaEye, FaEyeSlash, FaDice } from "react-icons/fa"; + + + + +interface SecretProps { + appName: string; +} + +export const SecretsPage: React.FC = ({ appName }) => { + const [secrets, setSecrets] = useState(); + const [editingSecrets, setEditingSecrets] = useState(new Set()); + const [secretValues, setSecretValues] = useState({}); + const [hidden, setHidden] = useState(new Set()); + const [loadingSecrets, setLoadingSecrets] = useState(new Set()); + const [ellipses, setEllipses] = useState(""); + + useEffect(() => { + const interval = setInterval(() => { + setEllipses(prev => { + if (prev.length >= 3) return ""; + return prev + "."; + }); + }, 500); + + return () => clearInterval(interval); + }, []); + + const copyToClipboard = async (value: string) => { + if (!value) return; + await navigator.clipboard.writeText(value); + }; + + useEffect(() => { + document.body.classList.add("modal-open"); + + return () => { + document.body.classList.remove("modal-open"); + }; + }, []); + useEffect(() => { + const fetchSecrets = async () => { + const secretInfo = await apiService.getSecrets(appName); + setSecrets(secretInfo); + } + fetchSecrets(); + }, []); + const generateSecret = (name: string) => { + setSecretValues(prev => ({ + ...prev, + [name]: generatePassphrase(12).join("-"), + })) + }; + const generateOrUpdateSecret = async (name: string, version: string, value: string) => { + // TODO: handle updating secret instead of generating + console.log(appName, name, version, value); + setEditingSecrets(prev => { + const next = new Set(prev); + next.delete(name); + return next; + }); + setSecretValues(prev => { + const next = { ...prev }; + delete next[name]; + return next; + }); + if (hidden.has(name)) { + setHidden(prev => { + const next = new Set(prev); + next.delete(name); + return next; + }); + } + setLoadingSecrets(prev => new Set(prev).add(name)); + try { + await apiService.writeSecret(appName, name, version, value); + const secretInfo = await apiService.getSecrets(appName); + setSecrets(secretInfo); + } catch (err) { + // TODO: better error handling + console.log("Error in setting new secret") + } + setLoadingSecrets(prev => { + const next = new Set(prev); + next.delete(name); + return next; + }); + }; + + return ( +
+ {(secrets == null &&

Fetching Secrets...

)} + {(secrets != null && secrets.length == 0 &&

Found no secrets

)} + {(secrets != null && secrets.length > 0 && +
+ + + + + + + + + + {secrets.map((secret) => ( + + + + + + + ))} + +
Secret Name Version Status
{secret.name}{secret.version} + {loadingSecrets.has(secret.name) &&
modifying secret{ellipses}
} + {!loadingSecrets.has(secret.name) && (
+ {!editingSecrets.has(secret.name) && !loadingSecrets.has(secret.name) && {secret.created ? "created" : "not created"} } + {editingSecrets.has(secret.name) && +
+ + + setSecretValues(prev => ({ + ...prev, + [secret.name]: e.target.value, + })) + } + /> +
+ + +
+
+ } + + + { editingSecrets.has(secret.name) && + + } +
)} +
+ {secret.value} + +
+
)} +
+ ); +}; \ No newline at end of file diff --git a/web/src/components/Header/Header.tsx b/web/src/components/Header/Header.tsx new file mode 100644 index 0000000..3cc8ff2 --- /dev/null +++ b/web/src/components/Header/Header.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import './_Header.scss'; +import logo from '../../assets/coopcloud_logo_grey.svg'; + + +interface HeaderProps { + children: React.ReactNode; +} + +export const Header: React.FC = ({ children }) => { + const navigate = useNavigate(); +return( +
+
+

navigate('/dashboard')} className="logo"> + +

+ +
+
+)} \ No newline at end of file diff --git a/web/src/components/Header/_Header.scss b/web/src/components/Header/_Header.scss new file mode 100644 index 0000000..480d9ec --- /dev/null +++ b/web/src/components/Header/_Header.scss @@ -0,0 +1,83 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; + +.layout-header { + background-color: $primary-light; + color: $text-primary; + padding: $spacing-lg 0; + box-shadow: $shadow-lg; + + .header-content { + max-width: 1400px; + margin: 0 auto; + padding: 0 $spacing-xl; + display: flex; + justify-content: space-between; + align-items: center; + gap: $spacing-xl; + + @media (max-width: 768px) { + flex-direction: column; + gap: $spacing-md; + } + } + + .logo { + height: 100px; + margin: 0; + display: inline-block; + max-width: 100%; + cursor: pointer; + } + + .nav { + display: flex; + gap: $spacing-sm; + flex: 1; + text-align: center; + + @media (max-width: 768px) { + width: 100%; + justify-content: center; + } + } + + .nav-link { + background: none; + border: none; + color: $text-primary; + padding: $spacing-sm $spacing-lg; + cursor: pointer; + border-radius: $radius-md; + transition: all $transition-base; + font-size: $font-size-xl; + font-weight: $font-weight-medium; + position: relative; + + &:hover { + background-color: rgba(255, 255, 255, 0.15); + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + } + + // Active indicator + &.active::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 80%; + height: 2px; + background-color: $bg-primary; + } + + @media (max-width: 768px) { + padding: $spacing-sm $spacing-md; + font-size: $font-size-xl; + } + } +} \ No newline at end of file diff --git a/web/src/components/Loader/Loader.scss b/web/src/components/Loader/Loader.scss new file mode 100644 index 0000000..7e1e2a7 --- /dev/null +++ b/web/src/components/Loader/Loader.scss @@ -0,0 +1,20 @@ +@use '../../assets/scss/variables' as *; + +.loader { + display: inline-flex; // instead of block + align-items: center; // center SVG inside + line-height: 1; // prevents extra vertical space + svg { + display: block; + } + + rect { + fill: $primary-light; + } + + &--style7 { + rect { + transform-origin: center bottom; + } + } +} \ No newline at end of file diff --git a/web/src/components/Loader/Loader.tsx b/web/src/components/Loader/Loader.tsx new file mode 100644 index 0000000..1352508 --- /dev/null +++ b/web/src/components/Loader/Loader.tsx @@ -0,0 +1,35 @@ +import "./Loader.scss"; + +export const Loader = () => { + return ( +
+ + + + + + + + + + + + + + +
+ ); +}; diff --git a/web/src/components/Spinner/Spinner.scss b/web/src/components/Spinner/Spinner.scss new file mode 100644 index 0000000..6ceb45e --- /dev/null +++ b/web/src/components/Spinner/Spinner.scss @@ -0,0 +1,43 @@ +// magic spinner stuff + +$offset: 187; +$duration: 1.4s; + +.spinner { + margin-top: 10px; + animation: rotator $duration linear infinite; +} + +@keyframes rotator { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(270deg); } +} + +.path { + stroke-dasharray: $offset; + stroke-dashoffset: 0; + transform-origin: center; + animation: + dash $duration ease-in-out infinite, + colors ($duration*4) ease-in-out infinite; +} + +@keyframes colors { + 0% { stroke: #4285F4; } + 25% { stroke: #DE3E35; } + 50% { stroke: #F7C223; } + 75% { stroke: #1B9A59; } + 100% { stroke: #4285F4; } +} + +@keyframes dash { + 0% { stroke-dashoffset: $offset; } + 50% { + stroke-dashoffset: $offset/4; + transform:rotate(135deg); + } + 100% { + stroke-dashoffset: $offset; + transform:rotate(450deg); + } +} \ No newline at end of file diff --git a/web/src/components/Spinner/Spinner.tsx b/web/src/components/Spinner/Spinner.tsx new file mode 100644 index 0000000..c345fe8 --- /dev/null +++ b/web/src/components/Spinner/Spinner.tsx @@ -0,0 +1,9 @@ +import "./Spinner.scss" + +export const Spinner = () => { + return ( + + + + ); +}; diff --git a/web/src/components/Terminal/Terminal.tsx b/web/src/components/Terminal/Terminal.tsx new file mode 100644 index 0000000..9fd1b6a --- /dev/null +++ b/web/src/components/Terminal/Terminal.tsx @@ -0,0 +1,83 @@ +import React, { useEffect, useRef, useState } from 'react'; +import type { LogEntry } from '../../services/mockApi'; // Import from mockApi (or api for real) +import './_Terminal.scss'; + +interface TerminalProps { + logs: LogEntry[]; + isActive: boolean; + onClose?: () => void; +} + +export const Terminal: React.FC = ({ logs, isActive, onClose }) => { + const terminalRef = useRef(null); + const indexRef = useRef(0); + const [displayedLogs, setDisplayedLogs] = useState([]); + + // Stream logs in with delays for realistic effect + useEffect(() => { + if (!isActive || logs.length === 0) { + indexRef.current = 0; + setDisplayedLogs([]); + return; + } + + const streamLogs = () => { + if (indexRef.current < logs.length) { + setDisplayedLogs(prev => [...prev, logs[indexRef.current]]); + indexRef.current++; + + // Variable delay based on log type + const delay = logs[indexRef.current - 1]?.type === 'command' ? 200 : + logs[indexRef.current - 1]?.type === 'output' ? 100 : 300; + + setTimeout(streamLogs, delay); + } + }; + + streamLogs(); + }, [logs, isActive]); + // Auto-scroll to bottom + useEffect(() => { + if (terminalRef.current) { + terminalRef.current.scrollTop = terminalRef.current.scrollHeight; + } + }, [displayedLogs]); + + if (!isActive && displayedLogs.length === 0) { + return null; + } + + const formatTime = (date: Date) => { + return date.toLocaleTimeString('en-US', { + hour12: false, + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); + }; + + return ( +
+
+
+ + + +
+
abra CLI
+
+
+ +
+ {displayedLogs.filter(log => log && log.type).map((log, index) => ( +
+ {log.text} +
+ ))} + {isActive && displayedLogs.length > 0 && ( +
▊
+ )} +
+
+ ); +}; diff --git a/web/src/components/Terminal/_Terminal.scss b/web/src/components/Terminal/_Terminal.scss new file mode 100644 index 0000000..fc1579e --- /dev/null +++ b/web/src/components/Terminal/_Terminal.scss @@ -0,0 +1,204 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; + +.terminal-container { + background: #1e1e1e; + border-radius: $radius-md; + overflow: hidden; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Droid Sans Mono', 'Source Code Pro', monospace; + margin-bottom: $spacing-xl; + animation: terminal-appear 0.3s ease-out; +} + +@keyframes terminal-appear { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.terminal-header { + display: flex; + align-items: center; + padding: $spacing-sm $spacing-md; + background: #323232; + border-bottom: 1px solid #404040; + user-select: none; + + .terminal-controls { + display: flex; + gap: $spacing-xs; + min-width: 60px; + } + + .terminal-control { + width: 12px; + height: 12px; + border-radius: 50%; + cursor: pointer; + transition: opacity 0.2s; + + &:hover { + opacity: 0.8; + } + + &.close { + background: #ff5f56; + } + + &.minimize { + background: #ffbd2e; + } + + &.maximize { + background: #27c93f; + } + } + + .terminal-title { + flex: 1; + text-align: center; + color: #a0a0a0; + font-size: $font-size-xs; + font-weight: $font-weight-medium; + } + + .terminal-spacer { + min-width: 60px; + } +} + +.terminal-content { + padding: $spacing-md; + background: #1e1e1e; + color: #d4d4d4; + font-size: 13px; + line-height: 1.6; + max-height: 400px; + overflow-y: auto; + overflow-x: hidden; + + /* Custom scrollbar */ + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-track { + background: #2d2d2d; + } + + &::-webkit-scrollbar-thumb { + background: #4a4a4a; + border-radius: 4px; + + &:hover { + background: #5a5a5a; + } + } +} + +.terminal-line { + display: flex; + margin-bottom: 2px; + animation: terminal-line-appear 0.2s ease-out; + white-space: pre-wrap; + word-break: break-word; + + @keyframes terminal-line-appear { + from { + opacity: 0; + transform: translateX(-4px); + } + to { + opacity: 1; + transform: translateX(0); + } + } + + .terminal-timestamp { + color: #6a6a6a; + margin-right: $spacing-sm; + flex-shrink: 0; + font-size: 11px; + } + + .terminal-text { + flex: 1; + } + + /* Line type styles */ + &.terminal-info { + .terminal-text { + color: #4fc3f7; + } + } + + &.terminal-success { + .terminal-text { + color: #81c784; + font-weight: $font-weight-medium; + } + } + + &.terminal-error { + .terminal-text { + color: #e57373; + font-weight: $font-weight-medium; + } + } + + &.terminal-warning { + .terminal-text { + color: #ffb74d; + } + } + + &.terminal-command { + .terminal-text { + color: #ba68c8; + font-weight: $font-weight-semibold; + } + } + + &.terminal-output { + .terminal-text { + color: #d4d4d4; + opacity: 0.9; + } + } +} + +.terminal-cursor { + display: inline-block; + color: #81c784; + margin-left: 4px; + animation: terminal-cursor-blink 1s step-end infinite; +} + +@keyframes terminal-cursor-blink { + 0%, 50% { + opacity: 1; + } + 51%, 100% { + opacity: 0; + } +} + +/* Responsive */ +@media (max-width: 768px) { + .terminal-content { + font-size: 12px; + padding: $spacing-sm; + } + + .terminal-line { + .terminal-timestamp { + display: none; + } + } +} \ No newline at end of file diff --git a/web/src/index.scss b/web/src/index.scss new file mode 100644 index 0000000..f13b8e4 --- /dev/null +++ b/web/src/index.scss @@ -0,0 +1,93 @@ +@use './assets/scss/variables' as *; +@use './assets/scss/mixins' as *; +@use './assets/scss/global' as *; + +// Global root styles +:root { + line-height: 1.5; + font-weight: 400; + font-family: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', + 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + + color-scheme: light dark; + color: $text-primary; + background-color: $bg-primary; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Global element resets +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; +} + +// Global link styles +a { + font-weight: 500; + color: $primary; + text-decoration: none; + transition: color $transition-base; + + &:hover { + color: $primary-light; + } +} + +// Global heading styles +h1, h2, h3, h4, h5, h6 { + color: $text-primary; + margin: 0; +} + +h1 { + font-size: $font-size-3xl; + line-height: 1.1; +} + +h2 { + font-size: $font-size-2xl; + line-height: 1.2; +} + +h3 { + font-size: $font-size-xl; + line-height: 1.3; +} + +// Global button styles +button { + border-radius: $radius-md; + border: 1px solid transparent; + padding: $spacing-sm $spacing-lg; + font-size: $font-size-base; + font-weight: $font-weight-medium; + font-family: inherit; + background-color: $primary; + color: white; + cursor: pointer; + transition: all $transition-base; + + &:hover { + background-color: $primary-light; + } + + &:focus-visible { + outline: 2px solid $primary; + outline-offset: 2px; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} diff --git a/web/src/main.tsx b/web/src/main.tsx new file mode 100644 index 0000000..46c0846 --- /dev/null +++ b/web/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.scss' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/web/src/routes/Apps/App.scss b/web/src/routes/Apps/App.scss new file mode 100644 index 0000000..09ea4eb --- /dev/null +++ b/web/src/routes/Apps/App.scss @@ -0,0 +1,591 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; +@use "sass:color"; + + +.app-detail-page { + @extend .page-wrapper; +} + +.app-detail-content { + @extend .page-content; +} + +// App header section +.app-header { + @include card; + display: flex; + justify-content: space-between; + align-items: flex-start; + flex-wrap: wrap; + gap: $spacing-xl; + margin-bottom: $spacing-xl; + + .app-title-section { + flex: 1; + min-width: 300px; + + h1 { + margin: 0 0 $spacing-md 0; + font-size: $font-size-3xl; + color: $text-primary; + font-weight: $font-weight-bold; + } + + .app-meta { + display: flex; + gap: $spacing-sm; + flex-wrap: wrap; + } + } + + .app-actions { + display: flex; + gap: $spacing-md; + flex-wrap: wrap; + } +} + +// Action buttons +.action-btn { + @include action-btn(2px, $radius-md, $spacing-sm $spacing-lg, $font-size-sm); + + &.primary { + background: $primary; + color: white; + border-color: $primary-light; + + &:hover:not(:disabled) { + background: $primary-light; + border-color: $primary-light; + } + } + &.service { + &:hover:not(:disabled) { + background: #3fff5c9d; + + } + } + &.secondary { + background: $bg-secondary; + color: $text-primary; + border-color: $border-color; + } + + &.danger { + background: $error; + color: white; + border-color: $error; + + &:hover:not(:disabled) { + background: color.adjust($error, $lightness: -10%); + } + } + &.modify { + background: $warning; + color: white; + border-color: $warning; + white-space: nowrap; + + &:hover:not(:disabled) { + background: color.adjust($warning, $lightness: -10%); + } + } +} + +.back-button { + @extend .action-btn; + @extend .secondary; +} + +// Content grid layout +.content-grid { + display: grid; + grid-template-columns: 2fr 1fr; + gap: $spacing-xl; + + @media (max-width: 1024px) { + grid-template-columns: 1fr; + } +} +.backup-header { + display: flex; + justify-content: space-between; + align-items: flex-start; +} +// Info cards +.info-card { + @include card; + margin-bottom: $spacing-xl; + + &:last-child { + margin-bottom: 0; + } + + h2 { + margin: 0 0 $spacing-lg 0; + font-size: $font-size-xl; + color: $text-primary; + font-weight: $font-weight-bold; + } +} + +// service grid +.service-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 2fr)); + gap: $spacing-md; +} + +.service-card { + @include card; + transition: transform $transition-base, box-shadow $transition-base; + position: relative; + overflow: hidden; + padding: $spacing-md; + .service-name { + h3 { + font-size: $font-size-lg; + color: $text-primary; + font-weight: $font-weight-bold; + } + } + .service-row { + flex-grow: 1; + flex: 1; + + .service-value { + font-size: $font-size-sm; + display: -webkit-box; + } + } + &.dark-green { + background-color: #3fff5c; + } + &.green { + background-color: #3fff5c9d; + } + &.red { + background-color: #ff1313; + } + &.gray { + background-color: rgb(187, 187, 187) + } +} + +// Info grid +.info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: $spacing-lg; +} + +.info-item { + display: flex; + flex-direction: column; + gap: $spacing-xs; + + label { + font-size: $font-size-xs; + color: $text-secondary; + font-weight: $font-weight-medium; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + span { + font-size: $font-size-base; + color: $text-primary; + } + + .no-value { + color: $text-muted; + font-style: italic; + } + + .chaos-active { + background: color.adjust($info, $lightness: -10%); + font-weight: $font-weight-medium; + } +} + +.domain-link { + color: $primary-light; + text-decoration: none; + font-size: $font-size-base; + transition: color $transition-base; + + &:hover { + text-decoration: underline; + } +} + +.server-link { + background: none; + border: none; + color: $primary-light; + cursor: pointer; + padding: 0; + font-size: $font-size-base; + text-align: left; + transition: color $transition-base; + + &:hover { + background: none; + color: $primary-light; + text-decoration: underline; + } +} + +.server-link { + background: none; + border: none; + color: $primary-dark; + cursor: pointer; + padding: 0; + font-size: $font-size-base; + text-align: left; + transition: color $transition-base; + + &:hover { + color: $primary-light; + text-decoration: underline; + } +} + +// Version information +.version-info { + display: flex; + flex-direction: column; + gap: $spacing-lg; + + .version-current { + label { + display: block; + font-size: $font-size-xs; + color: $text-secondary; + font-weight: $font-weight-medium; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: $spacing-sm; + } + + code { + display: inline-block; + background: $bg-secondary; + padding: $spacing-sm $spacing-md; + border-radius: $radius-sm; + font-size: $font-size-sm; + color: $text-primary; + font-family: monospace; + } + } + + .version-upgrades { + label { + display: flex; + align-items: center; + gap: $spacing-sm; + font-size: $font-size-xs; + color: $text-secondary; + font-weight: $font-weight-medium; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: $spacing-md; + + .upgrade-count { + background: $warning; + color: white; + padding: 2px 8px; + border-radius: $radius-full; + font-size: $font-size-xs; + font-weight: $font-weight-bold; + } + } + + .upgrade-list { + display: flex; + flex-direction: column; + gap: $spacing-sm; + } + + .upgrade-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: $spacing-md; + padding: $spacing-md; + background: $bg-secondary; + border-radius: $radius-md; + border: 2px solid transparent; + transition: border-color $transition-base; + + &:hover { + border-color: $warning; + } + + code { + flex: 1; + font-size: $font-size-sm; + color: $text-primary; + font-family: monospace; + } + + .upgrade-btn { + padding: $spacing-xs $spacing-md; + background: $warning; + color: white; + border: none; + border-radius: $radius-sm; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + cursor: pointer; + transition: all $transition-base; + + &:hover:not(:disabled) { + background: color.adjust($warning, $lightness: -10%); + transform: translateY(-1px); + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } + } + } + } + + .version-latest { + padding: $spacing-md; + background: rgba($success, 0.1); + background: color.adjust($success, $lightness: -10%); + border-radius: $radius-md; + text-align: center; + font-size: $font-size-base; + font-weight: $font-weight-medium; + } +} +.backup-info { + display: grid; + align-items: center; + text-align: left; + grid-template-columns: 2fr 1fr 2fr 100px; + gap: $spacing-md; + +} +.backup-list { + max-height: 250px; + overflow-y: auto; + + .create-btn { + width: 100%; + background: none; + color: $text-secondary; + border: 2px solid $text-muted; + border-radius: $radius-md; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + cursor: pointer; + transition: all $transition-base; + + &:hover:not(:disabled) { + border-color: $warning; + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } + } + .backup-item { + display: grid; + align-items: center; + text-align: left; + grid-template-columns: 2fr 1fr 2fr 100px; + gap: $spacing-md; + background: $bg-secondary; + border-radius: $radius-md; + border: 2px solid transparent; + transition: border-color $transition-base; + + &:hover { + border-color: $warning; + } + + code { + font-size: $font-size-sm; + color: $text-primary; + font-family: monospace; + } + .backup-btn { + background: none; + color: $text-secondary; + border: none; + padding: $spacing-sm $spacing-md; + border-radius: $radius-sm; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + cursor: pointer; + transition: all $transition-base; + + &.delete-btn { + color: $error; + } + + &:hover:not(:disabled) { + transform: translateY(-1px); + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } + } + } +} +// Quick actions list +.action-list { + display: flex; + flex-direction: column; + gap: $spacing-sm; + + .action-list-item { + display: flex; + align-items: center; + gap: $spacing-md; + padding: $spacing-md; + background: $bg-secondary; + border: 2px solid transparent; + border-radius: $radius-md; + cursor: pointer; + transition: all $transition-base; + font-size: $font-size-sm; + color: $text-primary; + text-align: left; + width: 100%; + + &:hover:not(:disabled) { + background: white; + border-color: $primary; + transform: translateX(2px); + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &.danger { + &:hover:not(:disabled) { + border-color: $error; + background: rgba($error, 0.05); + } + } + .action-text { + flex: 1; + font-weight: $font-weight-medium; + } + } +} + +// Health status +.health-status { + display: flex; + flex-direction: column; + gap: $spacing-md; + + .health-item { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: $spacing-md; + border-bottom: 1px solid $border-color; + + &:last-child { + border-bottom: none; + padding-bottom: 0; + } + + .health-label { + font-size: $font-size-sm; + color: $text-secondary; + } + + .health-value { + font-size: $font-size-base; + font-weight: $font-weight-semibold; + color: $text-primary; + } + } +} + +// editor modal overlay +// locks scrolling +body.modal-open { + overflow: hidden; +} +.editor-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + .secrets { + height: auto; + width: auto; + } +} + +.editor-modal { + width: 90%; + height: 90%; + background: white; + display: flex; + flex-direction: column; + border-radius: 8px; + overflow: hidden; + + .editor-header { + padding: 10px; + border-bottom: 1px solid #ddd; + } + + .editor-body { + flex: 1; + overflow: auto; // 👈 THIS is key + display: flex; + + // make your code editor stretch + > * { + flex: 1; + } + } + .secrets { + height: auto; + width: auto; + } +} + +// navigation back to apps page +.back-navigation { + margin-bottom: 10px; +} +.rollback { + display: flex; + align-items: center; + gap: 8px; +} +.select-input { + padding: 8px 12px; + border: 1px solid #d0d7de; + border-radius: 6px; + background-color: #fff; + color: #24292f; + font-size: 14px; +} diff --git a/web/src/routes/Apps/App.tsx b/web/src/routes/Apps/App.tsx new file mode 100644 index 0000000..80be5e3 --- /dev/null +++ b/web/src/routes/Apps/App.tsx @@ -0,0 +1,760 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { useParams, useNavigate, Link } from 'react-router-dom'; +import { Header } from '../../components/Header/Header'; +import { Loader } from '../../components/Loader/Loader'; +import { Terminal } from '../../components/Terminal/Terminal'; +import { EditorPage } from '../../components/Editor/Editor'; +import { SecretsPage } from '../../components/Editor/Secrets'; +import { apiService } from '../../services/api'; +import { type AppSnapshot, type AbraApp, type AbraAppService, type AbraServiceState } from '../../types'; +import type { LogEntry } from '../../services/mockApi'; +import { createPortal } from "react-dom"; +import { ArchiveRestore, Download, Trash2, SquarePlus } from "lucide-react"; + +import './App.scss'; + +export const AppDetail: React.FC = () => { + const { server, appName } = useParams<{ server: string; appName: string }>(); + const navigate = useNavigate(); + + const [app, setApp] = useState(null); + const [deployState, setDeployState] = useState("undeployed | deploying | deployed | failed"); + const [serviceState, setServiceState] = useState>({}); + const [services, setServices] = useState(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [actionLoading, setActionLoading] = useState(null); + const [openEditor, setOpenEditor] = useState(false); + const [openSecrets, setOpenSecrets] = useState(false); + const [backupEnabled, setBackupEnabled] = useState(false); + const [backupSnapshots, setBackupSnapshots] = useState(); + const [rbVersion, setRbVersion] = useState("Auto"); + + // Terminal state + const [terminalLogs, setTerminalLogs] = useState([]); + const [terminalActive, setTerminalActive] = useState(false); + + // Stream state + const stopRef = useRef void)>(null); + const deployRef = useRef void)>(null); + + // App Information + const [versions, setVersions] = useState([]); + // Use to refresh page + const [refreshKey, setRefreshKey] = useState(0); + + const isMockMode = import.meta.env.VITE_MOCK_AUTH === 'true'; + const getServiceClass = (state: string, status: string) => { + if (state === "running" && status.includes("\(healthy\)")) return "service-card dark-green"; + if (status.includes("unhealthy")) return "service-card red" + if (state === "running") return "service-card green"; + return "service-card gray"; + }; + const getServiceClassDeploying = (state: string, status: string) => { + if (state.includes("converged") && status.includes("\(healthy\)")) return "service-card dark-green"; + if (status.includes("unhealthy")) return "service-card red" + if (state.includes("converged")) return "service-card green"; + return "service-card gray"; + }; + useEffect(() => { + const fetchApp = async () => { + try { + if (isMockMode) { + const { mockApiService } = await import('../../services/mockApi'); + const appsData = await mockApiService.getAppsGrouped(); + + const serverApps = appsData[server || '']; + const foundApp = serverApps?.apps.find(a => a.appName === appName); + + if (foundApp) { + setApp(foundApp); + } else { + setError('App not found'); + } + } else { + console.log('fetching app...'); + const appsData = await apiService.getAppsGrouped(); + if (appsData[server || ''].backupEnabled) { + setBackupEnabled(true); + } + const serverApps = appsData[server || '']; + const foundApp = serverApps?.apps.find(a => a.appName === appName); + + if (foundApp) { + setApp(foundApp); + const versionData = await apiService.getAppVersions(foundApp.appName); + if (versionData) { + setVersions(versionData); + } + // when the app is deploying it should handle setting the deploy state itself after success/failure. + if (deployState !== "deploying") { + setDeployState(foundApp.status === "deployed" ? "deployed" : "undeployed"); + } + } else { + setError('App not found'); + } + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load app'); + } finally { + setLoading(false); + } + }; + + fetchApp(); + }, [server, appName, isMockMode, refreshKey]); + // checks status of app containers + useEffect(() => { + let isMounted = true; + const heartbeat = async () => { + while (isMounted) { + if (deployState === "deployed" && appName) { + const services = await apiService.getServices(appName); + if (services) { + setServices(services); + } else { + setServices([]); + } + } + + await new Promise(resolve => setTimeout(resolve, 10000)); + } + }; + heartbeat(); + return () => { + isMounted = false; + }; + }, [deployState, appName]); + useEffect(() => { + const fetchSnapshots = async () => { + if (backupEnabled && app) { + console.log('test'); + // grab backupSnapshots here + const snapshots = await apiService.getBackupList(app.appName); + setBackupSnapshots(snapshots); + console.log(snapshots); + } + } + fetchSnapshots(); + }, [backupEnabled]) + function EditorModal({ onClose }) { + return createPortal( +
+
e.stopPropagation()} + > +
+ +
+ +
+ +
+
+
, + document.body + ); + } + function SecretsModal({ onClose }) { + return createPortal( +
+
e.stopPropagation()} + > +
+ +
+ +
+ +
+
+
, + document.body + ); + } + const waitForLogs = (appName: string) => { + setDeployState("deploying"); + console.log("deploying"); + deployRef.current = apiService.deployLogs(appName, + (update) => { + if (update.type === "service") { + console.log(update.data.name) + const serviceName = update.data.name.slice(appName.length+1) + setServiceState(prev => ({ + ...prev, + [serviceName]: { + ...prev[serviceName] ?? {}, + ...update.data + } + })) + } + if (update.type === "done") { + if (update.data.failed) { + setDeployState('failed'); + } else { + setDeployState("deployed"); + } + setRefreshKey(prev => prev + 1); + } + } + ) + } + // TODO + const handleDeleteSnapshot = async () => {} + const handleDownloadSnapshot = async (appName: string) => { + const a = document.createElement('a') + a.href = `http://localhost:3000/api/apps/${appName}/backup/download` + a.click() + // console.log("downloading...") + // const blob = await apiService.downloadSnapshot(appName) + + // const url = URL.createObjectURL(blob) + + // const a = document.createElement('a') + // a.href = url + // a.download = `${appName}.tar.gz` + // a.click() + + // URL.revokeObjectURL(url) + } + // RESTORE UNTESTED + const handleRestoreSnapshot = async (appName: string, snapshot?: string) => { + apiService.restoreBackup(appName, snapshot) + } + + const handleAction = async (action: string, version?: string) => { + if (!app) return; + + setActionLoading(action); + try { + if (isMockMode) { + const { mockApiService } = await import('../../services/mockApi'); + + const onLog = (log: LogEntry) => { + setTerminalLogs(prev => [...prev, log]); + }; + + switch (action) { + case 'deploy': + await mockApiService.deployApp(app.appName, onLog); + break; + case 'stop': + await mockApiService.stopApp(app.appName, onLog); + break; + case 'upgrade': + if (version) { + await mockApiService.upgradeApp(app.appName, version, onLog); + } + break; + case 'remove': + await mockApiService.removeApp(app.appName, onLog); + break; + } + } else { + // Real API calls + switch (action) { + case 'stop': + await apiService.stopApp(app.appName); + setRefreshKey(prev => prev + 1); + break; + case 'upgrade': + await apiService.upgradeApp(app.appName); + waitForLogs(app.appName); + break; + case 'deploy': + await apiService.deployApp(app.appName); + waitForLogs(app.appName); + break; + case 'logs': + setTerminalActive(true); + setTerminalLogs([]); + if (version) { + stopRef.current = apiService.getLogs(app.appName, version, + (line) => { + setTerminalLogs(prev => [...prev, { + type: 'info', + text: `${line}`, + timestamp: new Date() + }]); + } + ) + } + break; + case 'remove': + await apiService.removeApp(app.appName); + setApp(null); + break; + case 'config': + setOpenEditor(true); + break; + case 'secrets': + setOpenSecrets(true); + break; + case 'rollback': + if (version) { + console.log(version); + apiService.rollbackApp(app.appName, version); + waitForLogs(app.appName); + } else { + console.log("no version for rollback"); + } + } + } + } catch (err) { + console.error('Action failed:', err); + setTerminalLogs(prev => [...prev, { + type: 'error', + text: `❌ Error: ${err instanceof Error ? err.message : 'Action failed'}`, + timestamp: new Date() + }]); + } finally { + setActionLoading(null); + } + }; + + if (loading) { + return ( +
+
+
+
+ +
Loading application
+
+
+
+ ); + } + + if (error || !app) { + return ( +
+
+
+
{error || 'App not found'}
+ +
+
+ ); + } + // TODO: make sure this makes sense when app.upgrade is unknown + const upgradeVersions = (app.upgrade !== 'latest' && app.upgrade !== 'unknown') ? app.upgrade.split('\n') : []; + + return ( +
+ {openEditor && ( + setOpenEditor(false)} /> + )} + {openSecrets && ( + setOpenSecrets(false)} /> + )} +
+
+
+ +
+ +
+
+

{app.appName}

+
+ {app.recipe} + {app.status} + {app.chaos === 'true' && ( + Chaos + )} +
+
+ +
+ + +
+
+ + {/* Terminal Component */} + { + stopRef.current?.(); + stopRef.current = null; + setTerminalActive(false) + }} + /> + +
+ {/* Left Column - Main Info */} +
+
+

Application Details

+ +
+
+ + {app.appName} +
+ +
+ + {app.domain ? ( + + {app.domain} ↗ + + ) : ( + No domain configured + )} +
+ +
+ + + {app.server} ↗ + +
+ +
+ + {app.recipe} +
+ +
+ + + {app.status} + +
+ +
+ + + {app.chaos === 'true' ? '☠️ Enabled' : 'Disabled'} + +
+
+
+ {(deployState === "deployed" || deployState === "failed") && ( +
+

Service Information

+
+ {services === undefined || services?.length === 0 ? ( +
Loading services...
+ ) : ( + services.map((service) => ( +
+
+

{service.service}

+
+
+ {service.state} + {service.status} +
+ + +
+ )) + )} +
+
)} + {(deployState === "deploying") && ( +
+

Service Information

+
+ {serviceState === undefined || Object.keys(serviceState).length === 0 ? ( +
Preparing services...
+ ) : ( + Object.keys(serviceState).map((name) => ( +
+
+

{name}

+
+
+ {serviceState[name].status} + {serviceState[name].health} +
+ + +
+ )) + )} +
+
)} +
+

Version Information

+ +
+
+ + {app.version} +
+ + {app.chaosVersion !== 'unknown' && ( +
+ + {app.chaosVersion} +
+ )} + + {upgradeVersions.length > 0 && ( +
+ +
+ {upgradeVersions.map((version, idx) => ( +
+ {version} + +
+ ))} +
+
+ )} + + {app.upgrade === 'unknown' && ( +
+ +
+
+ None +
+
+
+ )} + {app.upgrade === 'latest' && ( +
+ ✓ Running latest version +
+ )} +
+ + +
+
+
+
+ {backupEnabled && ( +
+
+

Backups

+ + backupbot deployed + +
+ +
+ Time Created + Snapshot ID + Version + +
+
+ + {backupSnapshots?.map((snapshot, idx) => ( +
+ {snapshot.createdAt} + {snapshot.snapshot} + {snapshot.version} +
+ + + { /* Not convinced this is necesary... + */ } +
+
+ ))} +
+
+ )} +
+
+ + {/* Right Column - Actions & Stats */} +
+
+

Quick Actions

+ +
+ + + { /* TODO: make upgrade have force option*/} + + + + + + + +
+
+
+

Health Status

+
+
+ CPU Usage + — +
+
+ Memory + — +
+
+ Uptime + — +
+
+
+
+
+
+
+ ); +}; diff --git a/web/src/routes/Apps/Apps.scss b/web/src/routes/Apps/Apps.scss new file mode 100644 index 0000000..ead1337 --- /dev/null +++ b/web/src/routes/Apps/Apps.scss @@ -0,0 +1,167 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; + +// Extend global page wrapper +.apps-page { + @extend .page-wrapper; +} + +.apps-content { + @extend .page-content; +} + +// Compact stats row +.stats-row { + display: flex; + align-items: center; + gap: $spacing-md; + margin-bottom: $spacing-xl; + flex-wrap: wrap; +} + +.stat-chip { + @include chip(); + + &.active { + border-color: $primary-dark; + background: $bg-primary; + box-shadow: 0 0 0 3px rgba($primary-dark, 0.08); + } + + .stat-label { + color: $text-secondary; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + } + + .stat-value { + color: $text-primary; + font-size: $font-size-xl; + font-weight: $font-weight-bold; + min-width: 24px; + text-align: center; + } +} + +/* reset-filters-btn removed — outline on stat-chip indicates active filters */ + +// Apps table specific styles +.apps-table-container { + @include scrollable-card; + margin-bottom: $spacing-lg; +} + +.apps-table { + width: 100%; + border-collapse: collapse; + + thead { + background-color: $bg-tertiary; + + th { + padding: $spacing-md $spacing-lg; + text-align: left; + font-weight: $font-weight-semibold; + color: $text-primary; + font-size: $font-size-sm; + text-transform: uppercase; + letter-spacing: 0.5px; + border-bottom: 2px solid $border-color; + } + } + + tbody { + tr { + border-bottom: 1px solid $border-color; + transition: background-color $transition-base; + + &:hover { + background-color: rgba($primary, 0.05); + } + + &:last-child { + border-bottom: none; + } + } + + td { + padding: $spacing-md $spacing-lg; + font-size: $font-size-base; + color: $text-primary; + + &.no-results { + text-align: center; + padding: $spacing-3xl; + color: $text-secondary; + } + } + } +} + +// App table cell styles +.app-name-cell { + .app-name { + font-weight: $font-weight-medium; + color: $text-primary; + } + .domain-link { + color: $primary-dark; + text-decoration: none; + transition: color $transition-base; + + &:hover { + color: $primary-light; + text-decoration: underline; + } + } +} + + +.no-domain { + color: $text-muted; + font-style: italic; +} + +.version-cell { + display: flex; + align-items: center; + gap: $spacing-sm; + + .version { + font-family: monospace; + font-size: $font-size-sm; + color: $text-primary; + } + + .chaos-badge, + .upgrade-available { + font-size: $font-size-base; + } +} + +// Action buttons +.actions { + display: flex; + gap: $spacing-sm; + + .action-btn { + @include action-btn(1px, $radius-sm, $spacing-xs $spacing-md, $font-size-sm); + + &:hover { + background-color: $primary; + color: white; + border-color: $primary; + } + + &.upgrade { + border-color: $warning; + color: $warning; + + &:hover { + background-color: $warning; + color: white; + } + } + } +} \ No newline at end of file diff --git a/web/src/routes/Apps/Apps.tsx b/web/src/routes/Apps/Apps.tsx new file mode 100644 index 0000000..8c44776 --- /dev/null +++ b/web/src/routes/Apps/Apps.tsx @@ -0,0 +1,289 @@ + +// TODOS: +// make the two filters non-exlusive + + +import React, { useEffect, useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Header } from '../../components/Header/Header'; +import { Loader } from '../../components/Loader/Loader'; +import { apiService } from '../../services/api'; +import type { AbraApp, AppWithServer, ServerAppsResponse } from '../../types'; +import './Apps.scss'; + +export const Apps: React.FC = () => { + const navigate = useNavigate(); + const [appsData, setAppsData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [searchTerm, setSearchTerm] = useState(''); + const [filterServer, setFilterServer] = useState('all'); + const [showUpgradesOnly, setShowUpgradesOnly] = useState(false); + const [showChaosOnly, setShowChaosOnly] = useState(false); + + const isMockMode = import.meta.env.VITE_MOCK_AUTH === 'true'; + + useEffect(() => { + const fetchData = async () => { + try { + if (isMockMode) { + const { mockApiService } = await import('../../services/mockApi'); + const data = await mockApiService.getAppsGrouped(); + setAppsData(data); + } else { + const data = await apiService.getAppsGrouped(); + setAppsData(data); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load apps'); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [isMockMode]); + + // Flatten and enrich apps data + const allApps: AppWithServer[] = useMemo(() => { + if (!appsData) return []; + + return Object.entries(appsData).flatMap(([serverName, serverData]) => + serverData.apps.map(app => ({ + ...app, + serverStats: { + appCount: serverData.appCount, + upgradeCount: serverData.upgradeCount, + }, + })) + ); + }, [appsData]); + + // Get unique servers + const servers = useMemo(() => { + if (!appsData) return []; + return Object.keys(appsData); + }, [appsData]); + + // Filter apps (additive filters: upgrades AND chaos can be applied together) + const filteredApps = useMemo(() => { + return allApps.filter(app => { + const matchesSearch = + (app.appName || '').toLowerCase().includes(searchTerm.toLowerCase()) || + (app.recipe || '').toLowerCase().includes(searchTerm.toLowerCase()) || + (app.domain || '').toLowerCase().includes(searchTerm.toLowerCase()); + + const matchesServer = filterServer === 'all' || app.server === filterServer; + const matchesChaos = !showChaosOnly || app.chaos === 'true'; + const matchesUpgrade = !showUpgradesOnly || app.upgrade !== 'latest'; + + return matchesSearch && matchesServer && matchesChaos && matchesUpgrade; + }); + }, [allApps, searchTerm, filterServer, showUpgradesOnly, showChaosOnly]); + + const stats = useMemo(() => { + const total = allApps.length; + const needsUpgrade = allApps.filter(app => app.upgrade !== 'latest').length; + const chaosApps = allApps.filter(app => app.chaos === 'true').length; + const totalServers = servers.length; + + return { total, needsUpgrade, chaosApps, totalServers }; + }, [allApps, servers]); + + const toggleUpgrades = () => setShowUpgradesOnly(prev => !prev); + const toggleChaos = () => setShowChaosOnly(prev => !prev); + + if (loading) { + return ( +
+
+
+
+ +
Loading apps
+
+
+
+ ); + } + + if (error) { + return ( +
+
+
+
Error: {error}
+
+
+ ); + } + + return ( +
+
+
+
+

Applications

+

{stats.total} apps across {stats.totalServers} servers

+
+ + {/* Compact Stats Overview */} +
+ + + + + + +
+ + {/* Filters */} +
+ setSearchTerm(e.target.value)} + className="search-input" + /> + + +
+ + {/* Apps Table */} +
+ + + + + + + + + + {/* */} + + + + {filteredApps.length === 0 ? ( + + + + ) : ( + filteredApps.map((app) => ( + navigate(`/apps/${app.server}/${app.appName}`)} + style={{ cursor: 'pointer' }} + > + + + + + + + {/* */} + + )) + )} + +
App NameRecipeDomainServerVersionStatusActions
+ No apps found matching your filters +
+ {app.appName} + + {app.recipe} + + {app.domain ? ( + e.stopPropagation()} + > + {app.domain} + + ) : ( + — + )} + + {app.server} + +
+ {app.version} + {app.chaos === 'true' && ( + + )} + {app.upgrade !== 'latest' && ( + + + )} +
+
+ + {app.status} + + +
+ + {app.upgrade !== 'latest' && ( + + )} +
+
+
+ +
+ Showing {filteredApps.length} of {allApps.length} apps +
+
+
+ ); +}; \ No newline at end of file diff --git a/web/src/routes/Dashboard/Dashboard.tsx b/web/src/routes/Dashboard/Dashboard.tsx new file mode 100644 index 0000000..0294c34 --- /dev/null +++ b/web/src/routes/Dashboard/Dashboard.tsx @@ -0,0 +1,136 @@ +import React, { useEffect, useState } from 'react'; +import { Header } from '../../components/Header/Header'; +import { Loader } from '../../components/Loader/Loader'; +import { useNavigate } from 'react-router-dom'; +import { apiService } from '../../services/api'; +import type { AbraApp, AbraServer } from '../../types'; +import './_Dashboard.scss'; + +export const Dashboard: React.FC = () => { + const [apps, setApps] = useState([]); + const [servers, setServers] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const navigate = useNavigate(); + + const isMockMode = import.meta.env.VITE_MOCK_AUTH === 'true'; + + useEffect(() => { + const fetchData = async () => { + try { + if (isMockMode) { + // Use mock API in development + const { mockApiService } = await import('../../services/mockApi'); + const [appsData, serversData] = await Promise.all([ + mockApiService.getAppsGrouped(), + mockApiService.getServers(), + ]); + + // Flatten the grouped apps data + const flatApps = Object.values(appsData).flatMap(serverData => serverData.apps); + setApps(flatApps); + setServers(serversData); + } else { + // Use real API in production + const [appsData, serversData] = await Promise.all([ + apiService.getAppsGrouped(), + apiService.getServers(), + ]); + + // Flatten the grouped apps data + const flatApps = Object.values(appsData).flatMap(serverData => serverData.apps); + setApps(flatApps); + setServers(serversData); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load data'); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [isMockMode]); + + // Calculate stats + const deployedAppsCount = apps.filter(a => a.status === 'deployed').length; + const serversWithAppsCount = new Set(apps.map(a => a.server)).size; + + if (loading) { + return ( +
+
+
+
+ +
Loading dashboard
+
+
+
+ ); + } + + if (error) { + return ( +
+
+
+
Error: {error}
+
+
+ ); + } + + return ( +
+
+
+
+

Dashboard

+
+ +
+ + + +
+ +
+

Recent Applications

+
+ {apps.slice(0, 5).map((app, index) => ( +
navigate(`/apps/${app.server}/${app.appName}`)} + > +
+

{app.appName}

+

{app.domain || 'No domain'}

+

{app.server}

+
+ + {app.status} + +
+ ))} +
+
+
+
+ ); +}; diff --git a/web/src/routes/Dashboard/_Dashboard.scss b/web/src/routes/Dashboard/_Dashboard.scss new file mode 100644 index 0000000..dbbc5dc --- /dev/null +++ b/web/src/routes/Dashboard/_Dashboard.scss @@ -0,0 +1,59 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; + +// Extend global page wrapper +.dashboard-page { + @extend .page-wrapper; +} + +.dashboard-content { + @extend .page-content; +} + +// Dashboard-specific styles +.recent-apps { + margin-bottom: $spacing-2xl; + + h3 { + font-size: $font-size-2xl; + margin: 0 0 $spacing-lg; + color: $text-primary; + } +} + +.apps-list { + @include card-list-vertical; +} + +.app-item { + @include card; + @include card-hover-lift(-2px, $shadow-lg); + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + + .app-info { + flex: 1; + + h4 { + font-size: $font-size-lg; + color: $text-primary; + font-weight: $font-weight-semibold; + } + + .app-domain { + margin: 0 0 $spacing-xs; + color: $text-muted; + font-size: $font-size-sm; + } + + .app-server { + margin: 0; + font-size: $font-size-sm; + color: $text-secondary; + font-family: monospace; + } + } +} diff --git a/web/src/routes/Recipes/Recipe.scss b/web/src/routes/Recipes/Recipe.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/routes/Recipes/Recipe.tsx b/web/src/routes/Recipes/Recipe.tsx new file mode 100644 index 0000000..e69de29 diff --git a/web/src/routes/Recipes/RecipeForm.scss b/web/src/routes/Recipes/RecipeForm.scss new file mode 100644 index 0000000..be60cbb --- /dev/null +++ b/web/src/routes/Recipes/RecipeForm.scss @@ -0,0 +1,159 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; + +.recipe-form { + @include card; + max-width: 680px; + margin: 0 auto; + display: flex; + flex-direction: column; + gap: $spacing-lg; + padding: $spacing-lg; + + h2 { + margin: 0; + font-size: $font-size-xl; + color: $text-primary; + } + + .field { + display: flex; + flex-direction: column; + gap: $spacing-xs; + + &.field-inline { + flex-direction: row; + align-items: center; + gap: $spacing-md; + } + + label { + color: $text-secondary; + font-size: $font-size-sm; + display: flex; + flex-direction: column; + gap: $spacing-xs; + + input[type="text"], + input[type="email"], + input:not([type]), + select { + padding: $spacing-sm $spacing-md; + border: 2px solid $border-color; + border-radius: $radius-md; + font-size: $font-size-base; + background: $bg-primary; + color: $text-primary; + transition: border-color $transition-base, box-shadow $transition-base; + } + + input:focus, + select:focus { + outline: none; + border-color: $primary; + box-shadow: 0 0 0 4px rgba($primary, 0.06); + } + } + } + + .form-actions { + display: flex; + gap: $spacing-sm; + justify-content: flex-end; + + .action-btn { + @include action-btn(2px, $radius-md, $spacing-sm $spacing-lg, $font-size-sm); + } + } +} + +.checkbox-label { + display: inline-flex; + align-items: center; + gap: $spacing-sm; + font-size: $font-size-base; + color: $text-primary; + + input[type="checkbox"] { + width: 18px; + height: 18px; + } +} + +.form-subtitle { + margin: 0; + color: $text-secondary; + font-size: $font-size-sm; +} + +.form-error { + background: rgba($error, 0.08); + color: $error; + padding: $spacing-sm $spacing-md; + border-radius: $radius-sm; +} + +.select-input { + width: 100%; +} + +.modal { + background: white; + position: relative; + padding: 24px; + border-radius: 8px; + width: min(100%, 800px); + max-height: 90vh; +} + +.modal .close-btn { + position: absolute; + top: 10px; + right: 10px; + + cursor: pointer; + font-size: 20px; + line-height: 1; + + padding: 4px 8px; + border-radius: 4px; + background: rgba(255, 0, 0, 0.308); +} + +.modal .close-btn:hover { + background: rgba(255, 0, 0, 0.797); +} + +.modal .close-btn:focus { + outline: 2px solid #888; +} + +.secrets table { + width: 90%; + border-collapse: collapse; + margin-top: 10px; +} + +.secrets th, +.secrets td { + border: 1px solid #ddd; + padding: 8px; + text-align: left; +} + +.secrets th { + background-color: #f5f5f5; + font-weight: bold; +} + +.secrets tr:nth-child(even) { + background-color: #fafafa; +} + +.copy-btn { + margin-left: 10px; + padding: 2px 6px; + font-size: 12px; + cursor: pointer; +} diff --git a/web/src/routes/Recipes/RecipeForm.tsx b/web/src/routes/Recipes/RecipeForm.tsx new file mode 100644 index 0000000..edb1f65 --- /dev/null +++ b/web/src/routes/Recipes/RecipeForm.tsx @@ -0,0 +1,158 @@ +import { useState, useEffect } from "react"; +import { apiService } from '../../services/api'; +import type { AbraServer, AbraAppSecret } from '../../types'; +import './RecipeForm.scss'; + + +function RecipeForm({ recipe, onClose }) { + const [loading, setLoading] = useState(true); + const [submitted, setSubmitted] = useState(false); + const [servers, setServers] = useState([]); + const [secretData, setSecretData] = useState([]); + const [error, setError] = useState(''); + + useEffect(() => { + const fetchData = async () => { + try { + if (servers.length === 0) { + const [serversData] = await Promise.all([ + apiService.getServers(), + ]); + + setServers(serversData); + } + + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load servers'); + } finally { + setLoading(false); + } + }; + + fetchData(); + }); + const [formData, setFormData] = useState({ + server: "", + domain: "", + chaos: false, + secrets: false, + }); + + const handleChange = (e) => { + const {name, type, value, checked} = e.target; + setFormData((prev) => ({ + ...prev, + [name]: type === "checkbox" ? checked : value, + })); + console.log(formData); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + console.log("Submitting:", formData); + setSubmitted(true); + const generatedSecrets = await apiService.newApp(recipe.name, formData); + setSecretData(generatedSecrets); + }; + + return ( +
+ + {(secretData.length === 0 && submitted) && ( +

Creating new app...

+ ) + } + {(secretData.length > 0 && submitted) && ( +
+ + + + + + + + + {secretData.map((secret) => ( + + + + + ))} + +
NameValue
{secret.name} + {secret.value} + +
+
+ )} + {(submitted === false) && +
+

{recipe.name}

+

Configure and deploy this recipe.

+ { error &&
{error}
} + { loading ? (

Loading servers...

+ ) : ( +
+ +
+ )} +
+ +
+
+ +
+ +
+ +
+ + +
+ } +
+ ); +} + +export default RecipeForm; \ No newline at end of file diff --git a/web/src/routes/Recipes/Recipes.scss b/web/src/routes/Recipes/Recipes.scss new file mode 100644 index 0000000..0847379 --- /dev/null +++ b/web/src/routes/Recipes/Recipes.scss @@ -0,0 +1,154 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; + +// Extend global page wrapper +.recipes-page { + @extend .page-wrapper; +} + +.recipes-content { + @extend .page-content; +} + +// Recipes grid +.recipes-grid { + display: grid; + justify-content: stretch; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: $spacing-xl; + margin-bottom: $spacing-xl; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + } +} + +// Recipe card +.recipe-card { + @include card; + @include card-dimensions(320px, 180px); + row-gap: 1em; + grid-template-rows: 1fr 2fr auto 0.9fr; + transition: transform $transition-base, box-shadow $transition-base; + position: relative; + overflow: hidden; + max-width: 30em; + + .recipe-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: $spacing-lg; + padding-bottom: $spacing-md; + border-bottom: 2px solid $bg-secondary; + + .recipe-title { + h3 { + margin: 0 0 $spacing-xs; + font-size: $font-size-xl; + color: $text-primary; + font-weight: $font-weight-bold; + } + + .recipe-host { + font-size: $font-size-sm; + color: $text-muted; + font-family: monospace; + } + } + img { + width: clamp(20px, 3vw, 32px); + height: clamp(20px, 3vw, 32px); + object-fit: contain; + } + } + .card-tags { + display: flex; + gap: 6px; + margin-top: $spacing-sm; + height: $spacing-md; + flex-wrap: wrap; + } + .tag-category { + font-size: 12px; + padding: 2px 8px; + border-radius: 5px; + color: rgb(255, 255, 255); + font-weight: 700; + background-color: rgb(111, 128, 255); + + } + .tag-feature { + font-size: 12px; + padding: 2px 8px; + border-radius: 5px; + color: rgb(255, 255, 255); + font-weight: 700; + background-color: rgb(22, 180, 22); + + } + .recipe-stats { + flex-grow: 1; + flex: 1; + margin-bottom: $spacing-lg; + + .stat-row { + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + + } + } + + .recipe-actions { + display: flex; + gap: $spacing-sm; + .action-btn { + flex: 1; + @include action-btn(2px, $radius-md, $spacing-sm $spacing-md, $font-size-sm); + + &.primary { + background-color: $primary; + color: white; + border-color: $primary; + + &:hover { + background-color: $primary-light; + border-color: $primary-light; + } + } + } + } + + .recipe-alert { + background-color: rgba($warning, 0.1); + border-left: 4px solid $warning; + padding: $spacing-sm $spacing-md; + margin: 0 (-$spacing-xl) (-$spacing-xl); + display: flex; + align-items: center; + gap: $spacing-sm; + + .alert-icon { + font-size: $font-size-lg; + color: $warning; + } + + .alert-text { + font-size: $font-size-sm; + color: $text-primary; + font-weight: $font-weight-medium; + } + } +} +.modal-overlay { +position: fixed; +inset: 0; +background: rgba(0, 0, 0, 0.4); +display: flex; +justify-content: center; +align-items: center; +} diff --git a/web/src/routes/Recipes/Recipes.tsx b/web/src/routes/Recipes/Recipes.tsx new file mode 100644 index 0000000..eb91d33 --- /dev/null +++ b/web/src/routes/Recipes/Recipes.tsx @@ -0,0 +1,179 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Header } from '../../components/Header/Header'; +import { Loader } from '../../components/Loader/Loader'; +import { apiService } from '../../services/api'; +import type { AbraApp, AbraRecipe, AppWithServer } from '../../types'; +import RecipeForm from './RecipeForm.tsx' +import './Recipes.scss'; + +export const Recipes: React.FC = () => { + const navigate = useNavigate(); + const [recipesData, setRecipesData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [searchTerm, setSearchTerm] = useState(''); + const [filterServer, setFilterServer] = useState('all'); + const [filterStatus, setFilterStatus] = useState('all'); + const [selectedRecipe, setSelectedRecipe] = useState(null); + const [showForm, setShowForm] = useState(false); + + + + const isMockMode = import.meta.env.VITE_MOCK_AUTH === 'true'; + + useEffect(() => { + const fetchData = async () => { + try { + if (isMockMode) { + const { mockApiService } = await import('../../services/mockApi'); + const data = await mockApiService.getRecipes(); + console.log(data) + setRecipesData(data); + } else { + const data = await apiService.getRecipes(); + setRecipesData(data); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load apps'); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [isMockMode]); + + // Flatten and enrich apps data + const allRecipes: AbraRecipe[] = useMemo(() => { + if (!recipesData) return []; + + return recipesData; + }, [recipesData]); + + + // Filter recipes + const filteredRecipes = useMemo(() => { + return allRecipes.filter(recipe => { + const matchesSearch = + recipe.name.toLowerCase().includes(searchTerm.toLowerCase()); + + return matchesSearch; + }); + }, [allRecipes, searchTerm, filterServer, filterStatus]); + + const stats = useMemo(() => { + const total = allRecipes.length; + + return { total }; + }, [allRecipes]); + + if (loading) { + return ( +
+
+
+
+ +
Loading recipes
+
+
+
+ ); + } + + if (error) { + return ( +
+
+
+
Error: {error}
+
+
+ ); + } + + return ( +
+
+
+
+

Recipes

+

{stats.total} recipes

+
+ + {/* Filters */} +
+ setSearchTerm(e.target.value)} + className="search-input" + /> + +
+ + {/* Server Cards */} +
+ {filteredRecipes.length === 0 ? ( +
No recipes found matching your search
+ ) : ( + filteredRecipes.map((recipe) => ( +
+
+
+

{recipe.name}

+
+
+ {recipe.icon.length > 0 ? : null } +
+
+ +
+
+ {recipe.description} +
+
+ +
+ +
+
+ {recipe.features.backups.toLowerCase().includes("yes") ? Backups : null} + {recipe.features.healthcheck.toLowerCase().includes("yes") ? Healthcheck : null} + {recipe.category.length > 0 ? {recipe.category} : null} +
+
+ )) + )} +
+ {selectedRecipe && ( +
setSelectedRecipe(null)}> +
e.stopPropagation()}> + {} + setSelectedRecipe(null)} /> +
+
+ )} + +
+ Showing {filteredRecipes.length} of {allRecipes.length} apps +
+
+
+ ); +}; \ No newline at end of file diff --git a/web/src/routes/Servers/Server.scss b/web/src/routes/Servers/Server.scss new file mode 100644 index 0000000..ea53d47 --- /dev/null +++ b/web/src/routes/Servers/Server.scss @@ -0,0 +1,340 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; +@use "sass:color"; + +.server-detail-page { + @extend .page-wrapper; +} + +.server-detail-content { + @extend .page-content; +} + +// Server header section +.server-header { + @include card; + display: flex; + justify-content: space-between; + align-items: flex-start; + flex-wrap: wrap; + gap: $spacing-xl; + margin-bottom: $spacing-xl; + + .server-title-section { + flex: 1; + min-width: 300px; + + h1 { + margin: 0 0 $spacing-md 0; + font-size: $font-size-3xl; + color: $text-primary; + font-weight: $font-weight-bold; + } + + .server-meta { + display: flex; + gap: $spacing-sm; + flex-wrap: wrap; + } + } + + .server-actions { + display: flex; + gap: $spacing-md; + flex-wrap: wrap; + } +} + +// Action buttons (shared with App view, could be moved to global) +.action-btn { + @include action-btn(2px, $radius-md, $spacing-sm $spacing-lg, $font-size-sm); + + &.primary { + background: $primary; + color: white; + border-color: $primary; + + &:hover:not(:disabled) { + background: $primary-light; + border-color: $primary-light; + } + } + + &.secondary { + background: $bg-secondary; + color: $text-primary; + border-color: $border-color; + + &:hover:not(:disabled) { + background: color.adjust($bg-secondary, $lightness: -10%); + } + } + + &.danger { + background: $error; + color: white; + border-color: $error; + + &:hover:not(:disabled) { + background: color.adjust($error, $lightness: -10%); + } + } +} + +.back-button { + @extend .action-btn; + @extend .secondary; +} + +// Badges - host badge is specific to server view +.host-badge { + display: inline-flex; + align-items: center; + gap: $spacing-xs; + padding: $spacing-xs $spacing-md; + background: $bg-secondary; + color: $text-secondary; + border-radius: $radius-md; + font-size: $font-size-sm; + font-weight: $font-weight-medium; + font-family: monospace; +} + +.upgrade-badge { + display: inline-flex; + align-items: center; + gap: $spacing-xs; + padding: $spacing-xs $spacing-md; + background: rgba($warning, 0.1); + background: color.adjust($error, $lightness: -20%); + border-radius: $radius-md; + font-size: $font-size-sm; + font-weight: $font-weight-medium; +} + +// Content grid layout +.content-grid { + display: grid; + grid-template-columns: 2fr 1fr; + gap: $spacing-xl; + + @media (max-width: 1024px) { + grid-template-columns: 1fr; + } +} + +// Info cards +.info-card { + @include card; + margin-bottom: $spacing-xl; + + &:last-child { + margin-bottom: 0; + } + + h2 { + margin: 0 0 $spacing-lg 0; + font-size: $font-size-xl; + color: $text-primary; + font-weight: $font-weight-bold; + } +} + +// Info grid +.info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: $spacing-lg; +} + +.info-item { + display: flex; + flex-direction: column; + gap: $spacing-xs; + + label { + font-size: $font-size-xs; + color: $text-secondary; + font-weight: $font-weight-medium; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + span { + font-size: $font-size-base; + color: $text-primary; + } + + .stat-value { + font-size: $font-size-xl; + font-weight: $font-weight-bold; + color: $text-primary; + + &.warning { + background: color.adjust($warning, $lightness: -10%); + } + + &.chaos { + background: color.adjust($info, $lightness: -10%); + } + } + + .no-value { + color: $text-muted; + font-style: italic; + } +} + +// Apps list in server detail +.no-apps { + text-align: center; + padding: $spacing-2xl; + color: $text-secondary; + font-size: $font-size-base; +} + +.apps-list { + @include card-list-vertical; +} + +.app-item { + padding: $spacing-md; + background: $bg-secondary; + border-radius: $radius-md; + border: 2px solid transparent; + cursor: pointer; + transition: all $transition-base; + + &:hover { + border-color: $primary; + background: white; + transform: translateX(4px); + } + + .app-item-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: $spacing-md; + margin-bottom: $spacing-sm; + + .app-item-name { + font-size: $font-size-base; + font-weight: $font-weight-semibold; + color: $text-primary; + } + + .app-item-badges { + display: flex; + gap: $spacing-xs; + flex-wrap: wrap; + } + } + + .app-item-details { + display: flex; + align-items: center; + gap: $spacing-md; + font-size: $font-size-sm; + + .app-item-version { + color: $text-secondary; + font-family: monospace; + } + + .app-item-domain { + color: $primary; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + } +} + +// Quick actions list +.action-list { + display: flex; + flex-direction: column; + gap: $spacing-sm; + + .action-list-item { + display: flex; + align-items: center; + gap: $spacing-md; + padding: $spacing-md; + background: $bg-secondary; + border: 2px solid transparent; + border-radius: $radius-md; + cursor: pointer; + transition: all $transition-base; + font-size: $font-size-sm; + color: $text-primary; + text-align: left; + width: 100%; + + &:hover:not(:disabled) { + background: white; + border-color: $primary; + transform: translateX(2px); + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &.danger { + &:hover:not(:disabled) { + border-color: $error; + background: rgba($error, 0.05); + } + } + + .action-text { + flex: 1; + font-weight: $font-weight-medium; + } + } +} + +// Health status +.health-status { + display: flex; + flex-direction: column; + gap: $spacing-md; + + .health-item { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: $spacing-md; + border-bottom: 1px solid $border-color; + + &:last-child { + border-bottom: none; + padding-bottom: 0; + } + + .health-label { + font-size: $font-size-sm; + color: $text-secondary; + } + + .health-value { + font-size: $font-size-base; + font-weight: $font-weight-semibold; + color: $text-primary; + + &.status-online { + color: $success; + } + + &.status-offline { + color: $error; + } + } + } +} diff --git a/web/src/routes/Servers/Server.tsx b/web/src/routes/Servers/Server.tsx new file mode 100644 index 0000000..f895a51 --- /dev/null +++ b/web/src/routes/Servers/Server.tsx @@ -0,0 +1,378 @@ +import React, { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { Header } from '../../components/Header/Header'; +import { Loader } from '../../components/Loader/Loader'; +import { Terminal } from '../../components/Terminal/Terminal'; +import { apiService } from '../../services/api'; +import type { AbraServer, ServerAppsResponse } from '../../types'; +import type { LogEntry } from '../../services/mockApi'; +import './Server.scss'; + +interface ServerWithApps extends AbraServer { + apps: any[]; + appCount: number; + upgradeCount: number; + versionCount: number; + unversionedCount: number; + latestCount: number; +} + +export const Server: React.FC = () => { + const { serverName } = useParams<{ serverName: string }>(); + const navigate = useNavigate(); + + const [server, setServer] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [actionLoading, setActionLoading] = useState(null); + + // Terminal state + const [terminalLogs, setTerminalLogs] = useState([]); + const [terminalActive, setTerminalActive] = useState(false); + + const isMockMode = import.meta.env.VITE_MOCK_AUTH === 'true'; + + useEffect(() => { + const fetchServer = async () => { + try { + if (isMockMode) { + const { mockApiService } = await import('../../services/mockApi'); + const appsData = await mockApiService.getAppsGrouped(); + const serversData = await mockApiService.getServers(); + + const foundServer = serversData.find(s => s.name === serverName); + const serverApps = appsData[serverName || '']; + + if (foundServer && serverApps) { + setServer({ + ...foundServer, + apps: serverApps.apps, + appCount: serverApps.appCount, + upgradeCount: serverApps.upgradeCount, + versionCount: serverApps.versionCount, + unversionedCount: serverApps.unversionedCount, + latestCount: serverApps.latestCount, + }); + } else { + setError('Server not found'); + } + } else { + const appsData = await apiService.getAppsGrouped(); + const serversData = await apiService.getServers(); + + const foundServer = serversData.find(s => s.name === serverName); + const serverApps = appsData[serverName || '']; + + if (foundServer && serverApps) { + setServer({ + ...foundServer, + apps: serverApps.apps, + appCount: serverApps.appCount, + upgradeCount: serverApps.upgradeCount, + versionCount: serverApps.versionCount, + unversionedCount: serverApps.unversionedCount, + latestCount: serverApps.latestCount, + }); + } else { + setError('Server not found'); + } + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load server'); + } finally { + setLoading(false); + } + }; + + fetchServer(); + }, [serverName, isMockMode]); + + const handleAction = async (action: string) => { + if (!server) return; + + setActionLoading(action); + setTerminalActive(true); + setTerminalLogs([]); + + try { + if (isMockMode) { + const { mockApiService } = await import('../../services/mockApi'); + + const onLog = (log: LogEntry) => { + setTerminalLogs(prev => [...prev, log]); + }; + + switch (action) { + case 'refresh': + await mockApiService.refreshServer(server.name, onLog); + break; + case 'deploy-all': + await mockApiService.deployAllApps(server.name, server.appCount, onLog); + break; + case 'upgrade-all': + await mockApiService.upgradeAllApps(server.name, server.upgradeCount, onLog); + break; + } + } else { + // Real API calls + console.log(`Action: ${action} on server ${server.name}`); + } + } catch (err) { + console.error('Action failed:', err); + setTerminalLogs(prev => [...prev, { + type: 'error', + text: `❌ Error: ${err instanceof Error ? err.message : 'Action failed'}`, + timestamp: new Date() + }]); + } finally { + setActionLoading(null); + } + }; + + if (loading) { + return ( +
+
+
+
+ +
Loading server
+
+
+
+ ); + } + + if (error || !server) { + return ( +
+
+
+
{error || 'Server not found'}
+ +
+
+ ); + } + + const chaosApps = server.apps.filter(app => app.chaos === 'true'); + const runningApps = server.apps.filter(app => app.status === 'deployed' || app.status === 'running'); + + return ( +
+
+
+
+ +
+ +
+
+

{server.name}

+
+ {server.host} + {server.upgradeCount > 0 && ( + + {server.upgradeCount} upgrade{server.upgradeCount !== 1 ? 's' : ''} + + )} +
+
+ +
+ + +
+
+ + {/* Terminal Component */} + setTerminalActive(false)} + /> + +
+ {/* Left Column - Main Info */} +
+
+

Server Details

+ +
+
+ + {server.name} +
+ +
+ + {server.host} +
+ +
+ + {server.appCount} +
+ +
+ + {runningApps.length} +
+ +
+ + 0 ? 'stat-value warning' : 'stat-value'}> + {server.upgradeCount} + +
+ +
+ + 0 ? 'stat-value chaos' : 'stat-value'}> + {chaosApps.length} + +
+ +
+ + {server.latestCount} +
+ +
+ + {server.versionCount} +
+
+
+ +
+

Applications on this Server

+ + {server.apps.length === 0 ? ( +
No applications deployed on this server
+ ) : ( +
+ {server.apps.map((app, idx) => ( +
navigate(`/apps/${server.name}/${app.appName}`)} + > +
+ {app.appName} +
+ {app.recipe} + + {app.status} + + {app.chaos === 'true' && ( + ☠️ + )} + {app.upgrade !== 'latest' && ( + ⬆️ + )} +
+
+
+ v{app.version} + {app.domain && ( + e.stopPropagation()} + > + {app.domain} + + )} +
+
+ ))} +
+ )} +
+
+ + {/* Right Column - Actions & Stats */} +
+
+

Quick Actions

+ +
+ + + + + +
+
+ +
+

Server Health

+
+
+ Status + 🟢 Online +
+
+ CPU Usage + — +
+
+ Memory + — +
+
+ Disk Space + — +
+
+ Uptime + — +
+
+
+
+
+
+
+ ); +}; diff --git a/web/src/routes/Servers/Servers.scss b/web/src/routes/Servers/Servers.scss new file mode 100644 index 0000000..a6ec97c --- /dev/null +++ b/web/src/routes/Servers/Servers.scss @@ -0,0 +1,157 @@ +@use '../../assets/scss/variables' as *; +@use '../../assets/scss/mixins' as *; +@use '../../assets/scss/global' as *; + +// Extend global page wrapper +.servers-page { + @extend .page-wrapper; +} + +.servers-content { + @extend .page-content; +} + +// Servers grid +.servers-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: $spacing-xl; + margin-bottom: $spacing-xl; + + align-items: stretch; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + } +} + +// Server card +.server-card { + @include card; + @include card-hover-lift(-4px, $shadow-xl); + @include card-dimensions(320px, 180px); + display: flex; + flex-direction: column; + position: relative; + overflow: hidden; + + .server-header { + @include card-header-rule; + + .server-title { + @include card-title-stack; + } + + .server-status { + .status-indicator { + font-size: $font-size-xl; + + &.connected { + color: $success; + } + + &.disconnected { + color: $error; + } + } + } + } + + .server-stats { + flex: 1; + margin-bottom: $spacing-lg; + + .stat-row { + @include card-stat-row; + + // Highlighted rows + &.highlight { + @include card-row-highlight-bleed; + background-color: rgba($warning, 0.05); + + .stat-label { + font-weight: $font-weight-semibold; + } + + .stat-value { + font-weight: $font-weight-bold; + } + } + + &.chaos-row { + @include card-row-highlight-bleed(false); + background-color: rgba($info, 0.05); + + .stat-label { + font-weight: $font-weight-semibold; + } + + .stat-value { + font-weight: $font-weight-bold; + } + } + + .stat-label { + font-size: $font-size-sm; + color: $text-secondary; + display: flex; + align-items: center; + gap: $spacing-xs; + + .upgrade-icon, + .chaos-icon { + font-size: $font-size-base; + } + } + + .stat-value { + font-size: $font-size-lg; + color: $text-primary; + font-weight: $font-weight-semibold; + } + } + } + + .server-actions { + display: flex; + gap: $spacing-sm; + margin-bottom: $spacing-md; + + .action-btn { + flex: 1; + @include action-btn(2px, $radius-md, $spacing-sm $spacing-md, $font-size-sm); + + &.primary { + // background-color: $primary; + // color: white; + // border-color: $primary; + + &:hover { + background-color: $primary-light; + border-color: $primary-light; + } + } + } + } + + .server-alert { + background-color: rgba($warning, 0.1); + border-left: 4px solid $warning; + padding: $spacing-sm $spacing-md; + margin: 0 (-$spacing-xl) (-$spacing-xl); + display: flex; + align-items: center; + gap: $spacing-sm; + + .alert-icon { + font-size: $font-size-lg; + color: $warning; + } + + .alert-text { + font-size: $font-size-sm; + color: $text-primary; + font-weight: $font-weight-medium; + } + } +} diff --git a/web/src/routes/Servers/Servers.tsx b/web/src/routes/Servers/Servers.tsx new file mode 100644 index 0000000..6d3d84b --- /dev/null +++ b/web/src/routes/Servers/Servers.tsx @@ -0,0 +1,259 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Header } from '../../components/Header/Header'; +import { Loader } from '../../components/Loader/Loader'; +import { apiService } from '../../services/api'; +import type { AbraServer, ServerAppsResponse } from '../../types'; +import './Servers.scss'; + +interface ServerWithStats extends AbraServer { + appCount: number; + versionCount: number; + latestCount: number; + upgradeCount: number; + chaosCount: number; + backupEnabled: boolean; +} + +export const Servers: React.FC = () => { + const navigate = useNavigate(); + const [servers, setServers] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [searchTerm, setSearchTerm] = useState(''); + const [sortBy, setSortBy] = useState<'name' | 'apps' | 'upgrades'>('name'); + const [showUpgradesOnly, setShowUpgradesOnly] = useState(false); + const [showChaosOnly, setShowChaosOnly] = useState(false); + + const isMockMode = import.meta.env.VITE_MOCK_AUTH === 'true'; + + useEffect(() => { + const fetchData = async () => { + try { + let serversData, appsData; + + if (isMockMode) { + const { mockApiService } = await import('../../services/mockApi'); + [serversData, appsData] = await Promise.all([ + mockApiService.getServers(), + mockApiService.getAppsGrouped(), + ]); + } else { + [serversData, appsData] = await Promise.all([ + apiService.getServers(), + apiService.getAppsGrouped(), + ]); + } + + // Enrich servers with stats from apps data + const enrichedServers = serversData.map(server => { + const serverStats = appsData[server.name]; + const chaosCount = serverStats?.apps.filter(app => app.chaos === 'true').length || 0; + + return { + ...server, + appCount: serverStats?.appCount || 0, + versionCount: serverStats?.versionCount || 0, + latestCount: serverStats?.latestCount || 0, + upgradeCount: serverStats?.upgradeCount || 0, + chaosCount, + backupEnabled: serverStats?.backupEnabled + }; + }); + + setServers(enrichedServers); + } catch (err) { + console.error('Error loading servers:', err); + setError(err instanceof Error ? err.message : 'Failed to load servers'); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [isMockMode]); + + // Calculate overall stats + const stats = useMemo(() => { + const totalServers = servers.length; + const totalApps = servers.reduce((sum, s) => sum + s.appCount, 0); + const totalUpgrades = servers.reduce((sum, s) => sum + s.upgradeCount, 0); + const totalChaos = servers.reduce((sum, s) => sum + s.chaosCount, 0); + + return { totalServers, totalApps, totalUpgrades, totalChaos }; + }, [servers]); + + // Filter and sort servers (additive filters allowed) + const filteredServers = useMemo(() => { + const filtered = servers.filter(server => { + const matchesSearch = + server.name.toLowerCase().includes(searchTerm.toLowerCase()) || + server.host.toLowerCase().includes(searchTerm.toLowerCase()); + const matchesUpgrades = !showUpgradesOnly || server.upgradeCount > 0; + const matchesChaos = !showChaosOnly || server.chaosCount > 0; + return matchesSearch && matchesUpgrades && matchesChaos; + }); + + filtered.sort((a, b) => { + switch (sortBy) { + case 'apps': + return b.appCount - a.appCount; + case 'name': + default: + return a.name.localeCompare(b.name); + } + }); + + return filtered; +}, [servers, searchTerm, sortBy, showUpgradesOnly]); + + if (loading) { + return ( +
+
+
+
+ +
Loading servers
+
+
+
+ ); + } + + if (error) { + return ( +
+
+
+
Error: {error}
+
+
+ ); + } + + return ( +
+
+
+
+

Servers

+

Managing {stats.totalServers} servers with {stats.totalApps} applications

+
+ + {/* Compact Stats Row */} +
+ + + + + + + {/* Clear filters button removed — use stat-chip outlines for active filters */} +
+ + {/* Filters */} +
+ setSearchTerm(e.target.value)} + className="search-input" + /> +
+ + {/* Server Cards */} +
+ {filteredServers.length === 0 ? ( +
No servers found matching your search
+ ) : ( + filteredServers.map((server) => ( +
navigate(`/servers/${server.name}`)} + style={{ cursor: 'pointer' }} + > +
+
+

{server.name}

+ {server.host} +
+
+ ● +
+
+ +
+
+ Applications + {server.appCount} +
+
+ Versioned + {server.versionCount} +
+
+ Latest + {server.latestCount} +
+ {server.upgradeCount > 0 && ( +
+ + Need Upgrade + + {server.upgradeCount} +
+ )} + {server.chaosCount > 0 && ( +
+ + Chaos Mode + + {server.chaosCount} +
+ )} +
+ {!server.backupEnabled && ( +
+ + No Backups Enabled + +
+ )} +
+ )) + )} +
+ +
+ Showing {filteredServers.length} of {servers.length} servers +
+
+
+ ); +}; diff --git a/web/src/services/api.ts b/web/src/services/api.ts new file mode 100644 index 0000000..171b164 --- /dev/null +++ b/web/src/services/api.ts @@ -0,0 +1,287 @@ +import type { AbraServer, AbraRecipe, ServerAppsResponse, AbraAppService, DeployEvent, AbraAppSecret, File, AppSnapshot } from '../types'; + +// Log entry type +export type LogEntry = { + type: 'info' | 'success' | 'error' | 'warning' | 'command' | 'output'; + text: string; + timestamp: Date; +}; + +const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api'; + +// Helper to process log JSON from API response +const processLogResponse = (logData: any[]): LogEntry[] => { + return logData.map(log => ({ + type: log.type, + text: log.text, + timestamp: new Date(log.timestamp || Date.now()), + })); +}; + +class ApiService { + private async request( + endpoint: string, + options: RequestInit = {}, + URLQueryString?: string + ): Promise { + const headers: HeadersInit = { + 'Content-Type': 'application/json', + ...options.headers, + }; + const queryString = URLQueryString ?? "" + const response = await fetch(`${API_BASE_URL}${endpoint}` + queryString, { + ...options, + headers, + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({ message: 'An error occurred' })); + throw new Error(error.message || `HTTP ${response.status}`); + } + if (response.status === 204) { + return undefined as T; + } + + const contentType = response.headers.get('content-type'); + + if (contentType?.includes('application/json')) { + return response.json(); + } + return response.text() as unknown as T; + } + + private stream( + endpoint: string, + handlers: { + onMessage: (data: T) => void; + onError?: (err: any) => void; + onOpen?: () => void; + parser?: (raw: string) => T; + } + ) { + const es = new EventSource(`${API_BASE_URL}${endpoint}`); + + es.onopen = () => { + handlers.onOpen?.(); + }; + es.onmessage = (event) => { + try { + const data = handlers.parser + ? handlers.parser(event.data) + : (event.data as unknown as T); + + handlers.onMessage(data); + } catch (err) { + handlers.onError?.(err); + } + }; + es.onerror = (err) => { + handlers.onError?.(err); + es.close(); + }; + return () => es.close(); + } + private async file( + endpoint: string, + options: RequestInit = {} + ): Promise { + const headers: HeadersInit = { + 'Content-Type': 'application/json', + ...options.headers, + }; + const response = await fetch(`${API_BASE_URL}${endpoint}`, { + ...options, + headers, + }) + + if (!response.ok) { + const error = await response.json().catch(() => ({ message: 'An error occurred' })); + throw new Error(error.message || `HTTP ${response.status}`); + } + return response.blob() + } + // Get Logs for service + getLogs(appName: string, serviceName: string, msgHandler: (data: String) => void) { + return this.stream(`/apps/${appName}/${serviceName}/logs`, {onMessage: msgHandler}) + } + + async getConfig(appName: string): Promise { + const file = await this.request(`/apps/${appName}/config`) + return file.content + } + + async writeConfig(appName: string, code: string): Promise { + const file : File = { content: code }; + return this.request(`/apps/${appName}/config`, { + method: 'POST', + headers: { + 'Accept': 'application/json' + }, + body: JSON.stringify(file), + }) + } + + async getSecrets(appName: string): Promise { + return this.request(`/apps/${appName}/secret`); + } + + async writeSecret(appName: string, secretName: string, version: string, value: string): Promise { + console.log("writing secret ", appName, secretName, version, value); + return this.request(`/apps/${appName}/secret`, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify( { + 'name': secretName, + 'version': version, + 'value': value, + }) + }); + } + async downloadSnapshot(appName: string): Promise { + return this.file(`/apps/${appName}/backup/download`) + } + + // Get all apps grouped by server + async getAppsGrouped(): Promise { + return this.request('/apps'); + } + + // Get all servers + async getServers(): Promise { + return this.request('/servers'); + } + // Get services for app + async getServices(appName: string): Promise { + return this.request(`/apps/${appName}/services`); + } + + // Get all backups for an app + async getBackupList(appName: string): Promise { + return this.request(`/apps/${appName}/backup/list`); + } + // Create backup for an app + async createBackup (appName: string): Promise { + return this.request(`/apps/${appName}/backup/create`, { + method: 'POST', + }); + } + // restore backup for app + async restoreBackup (appName: string, snapshot?: string): Promise { + const response = await this.request(`/apps/${appName}/backup/restore`, { + method: 'POST', + body: JSON.stringify({snapshot: snapshot}), + }); + return response + } + // App actions with log streaming (websocket future) + async deployApp(appName: string): Promise { + return this.request(`/apps/${appName}/deploy`) + } + deployLogs(appName: string, msgHandler: (data: DeployEvent) => void) { + return this.stream(`/apps/${appName}/deploy`, {parser: JSON.parse, onMessage: msgHandler}) + } + + async stopApp(appName: string, onLog?: (log: LogEntry) => void): Promise { + const response = await this.request<{ logs: any[] }>(`/apps/${appName}/stop`, { + method: 'POST', + }); + + if (onLog && response.logs) { + const logs = processLogResponse(response.logs); + logs.forEach(log => onLog(log)); + } + } + + async upgradeApp(appName: string, version?: string, force?: boolean, onLog?: (log: LogEntry) => void): Promise { + const response = await this.request<{ logs: any[] }>(`/apps/${appName}/upgrade`, { + method: 'POST', + body: JSON.stringify({ version: version }), + }, + "?" + new URLSearchParams({force: force ? 'true' : 'false'}) + ); + + if (onLog && response.logs) { + const logs = processLogResponse(response.logs); + logs.forEach(log => onLog(log)); + } + } + + async removeApp(appName: string, onLog?: (log: LogEntry) => void): Promise { + const response = await this.request<{ logs: any[] }>(`/apps/${appName}/remove`, { + method: 'POST', + }); + + if (onLog && response.logs) { + const logs = processLogResponse(response.logs); + logs.forEach(log => onLog(log)); + } + } + async rollbackApp(appName: string, version: string, onLog?: (log: LogEntry) => void): Promise { + const response = await this.request<{ logs: any[] }>(`/apps/${appName}/rollback`, { + method: 'POST', + body: JSON.stringify({ version: version }), + }); + + if (onLog && response.logs) { + const logs = processLogResponse(response.logs); + logs.forEach(log => onLog(log)); + } + } + async newApp(appName: string, formData: Object): Promise { + const response = await this.request(`/apps/${appName}/new`, { + method: 'POST', + headers: { + 'Accept': 'application/json' + }, + body: JSON.stringify(formData) + }); + return response + } + async getAppVersions(app: string): Promise { + return this.request(`/apps/${app}/versions`); + } + + // Server actions with log streaming + async refreshServer(serverName: string, onLog?: (log: LogEntry) => void): Promise { + const response = await this.request<{ logs: any[] }>(`/servers/${serverName}/refresh`, { + method: 'POST', + }); + + if (onLog && response.logs) { + const logs = processLogResponse(response.logs); + logs.forEach(log => onLog(log)); + } + } + + async deployAllApps(serverName: string, appCount: number, onLog?: (log: LogEntry) => void): Promise { + const response = await this.request<{ logs: any[] }>(`/servers/${serverName}/deploy-all`, { + method: 'POST', + }); + + if (onLog && response.logs) { + const logs = processLogResponse(response.logs); + logs.forEach(log => onLog(log)); + } + } + + async upgradeAllApps(serverName: string, upgradeCount: number, onLog?: (log: LogEntry) => void): Promise { + const response = await this.request<{ logs: any[] }>(`/servers/${serverName}/upgrade-all`, { + method: 'POST', + }); + + if (onLog && response.logs) { + const logs = processLogResponse(response.logs); + logs.forEach(log => onLog(log)); + } + } + // recipe catalog imports + async getRecipes(): Promise { + return this.request('/catalogue'); + } +} + + +export const apiService = new ApiService(); \ No newline at end of file diff --git a/web/src/services/mock-apps.json b/web/src/services/mock-apps.json new file mode 100644 index 0000000..c3edd8f --- /dev/null +++ b/web/src/services/mock-apps.json @@ -0,0 +1,593 @@ +{ + "mydomain.com": { + "apps": [ + { + "server": "mydomain.com", + "recipe": "authentik", + "appName": "accounts.mydomain.com", + "domain": "accounts.mydomain.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "7.4.0+2025.6.3", + "upgrade": "9.0.1+2025.8.1\n9.0.0+2025.8.1\n8.0.0+2025.8.1\n7.4.1+2025.6.4" + }, + { + "server": "mydomain.com", + "recipe": "backup-bot-two", + "appName": "backup.mydomain.com", + "domain": "", + "status": "deployed", + "chaos": "false", + "chaosVersion": "2.3.0+2.3.0-beta", + "version": "2.3.0+2.3.0-beta", + "upgrade": "latest" + }, + { + "server": "mydomain.com", + "recipe": "collabora", + "appName": "docs.mydomain.com", + "domain": "docs.mydomain.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "3.2.0+24.04.12.3.1", + "version": "3.2.0+24.04.12.3.1", + "upgrade": "4.0.0+25.04.4.1.1\n3.3.0+24.04.13.3.1" + }, + { + "server": "mydomain.com", + "recipe": "nextcloud", + "appName": "nc.mydomain.com", + "domain": "nc.mydomain.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "12.0.1+31.0.6-fpm", + "upgrade": "latest" + }, + { + "server": "mydomain.com", + "recipe": "traefik", + "appName": "traefik.mydomain.com", + "domain": "traefik.mydomain.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "3.4.2+v3.4.5", + "upgrade": "3.6.2+v3.4.5\n3.6.1+v3.4.5\n3.6.0+v3.4.5\n3.5.0+v3.4.5" + } + ], + "appCount": 5, + "versionCount": 5, + "unversionedCount": 0, + "latestCount": 2, + "upgradeCount": 3 + }, + "coop.test.org": { + "apps": [ + { + "server": "coop.test.org", + "recipe": "cryptpad", + "appName": "cryptpad.coop.test.org", + "domain": "cryptpad.coop.test.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "cb2a47fb", + "version": "0.4.0+version-2024.3.0", + "upgrade": "latest" + }, + { + "server": "coop.test.org", + "recipe": "garage", + "appName": "garage.coop.test.org", + "domain": "garage.coop.test.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "3a729d56+U", + "version": "3a729d56+U", + "upgrade": "latest" + }, + { + "server": "coop.test.org", + "recipe": "mobilizon", + "appName": "mobilizon.test.org", + "domain": "mobilizon.test.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "f8f874a5 + unstaged changes", + "version": "0.2.1+5.1.2", + "upgrade": "latest" + }, + { + "server": "coop.test.org", + "recipe": "owncast", + "appName": "tv.test.org", + "domain": "tv.test.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "0.3.0+0.2.1", + "version": "0.3.0+0.2.1", + "upgrade": "latest" + }, + { + "server": "coop.test.org", + "recipe": "privatebin", + "appName": "paste.test.org", + "domain": "paste.test.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "0.6.1+0.17.4", + "version": "0.6.0+1.7.6", + "upgrade": "0.6.1+0.17.4" + }, + { + "server": "coop.test.org", + "recipe": "traefik", + "appName": "traefik.coop.test.org", + "domain": "traefik.coop.test.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "27d5c092", + "version": "27d5c092", + "upgrade": "latest" + } + ], + "appCount": 6, + "versionCount": 6, + "unversionedCount": 0, + "latestCount": 5, + "upgradeCount": 1 + }, + "internal.website.com": { + "apps": [ + { + "server": "internal.website.com", + "recipe": "backup-bot-two", + "appName": "backup.internal.website.com", + "domain": "backup.internal.website.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "2.3.0+2.3.0-beta", + "upgrade": "latest" + }, + { + "server": "internal.website.com", + "recipe": "baserow", + "appName": "base.website.com", + "domain": "base.website.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "1.1.0+1.31.1", + "upgrade": "1.2.1+1.35.3\n1.2.0+1.35.3" + }, + { + "server": "internal.website.com", + "recipe": "traefik", + "appName": "traefik.internal.website.com", + "domain": "traefik.internal.website.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "3.6.2+v3.4.5", + "upgrade": "latest" + } + ], + "appCount": 3, + "versionCount": 3, + "unversionedCount": 0, + "latestCount": 2, + "upgradeCount": 1 + }, + "internal.server.net": { + "apps": [ + { + "server": "internal.server.net", + "recipe": "authentik", + "appName": "accounts.server.net", + "domain": "accounts.server.net", + "status": "deployed", + "chaos": "false", + "chaosVersion": "6.11.0+2024.10.5", + "version": "6.11.0+2024.10.5", + "upgrade": "9.0.1+2025.8.1\n9.0.0+2025.8.1\n8.0.0+2025.8.1\n7.4.1+2025.6.4\n7.4.0+2025.6.3\n7.3.2+2025.6.2\n7.3.1+2025.6.1\n7.3.0+2025.6.0\n7.2.0+2025.4.1\n7.1.0+2025.2.4\n7.0.3+2025.2.3\n7.0.2+2025.2.2\n7.0.1+2025.2.0\n7.0.0+2025.2.0\n6.12.0+2024.12.3\n6.11.1+2024.10.5" + }, + { + "server": "internal.server.net", + "recipe": "authentik", + "appName": "accounts.test.server.net", + "domain": "accounts.test.server.net", + "status": "deployed", + "chaos": "false", + "chaosVersion": "7.1.0+2025.2.4", + "version": "7.1.0+2025.2.4", + "upgrade": "9.0.1+2025.8.1\n9.0.0+2025.8.1\n8.0.0+2025.8.1\n7.4.1+2025.6.4\n7.4.0+2025.6.3\n7.3.2+2025.6.2\n7.3.1+2025.6.1\n7.3.0+2025.6.0\n7.2.0+2025.4.1" + }, + { + "server": "internal.server.net", + "recipe": "backup-bot-two", + "appName": "backup.server.net", + "domain": "", + "status": "deployed", + "chaos": "false", + "chaosVersion": "2.3.0+2.3.0-beta", + "version": "2.3.0+2.3.0-beta", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "custom-html", + "appName": "counterspy.zip", + "domain": "counterspy.zip", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "1.11.0+1.29.0", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "garage", + "appName": "garage.server.net", + "domain": "garage.server.net", + "status": "deployed", + "chaos": "true", + "chaosVersion": "20bfd2c6+U", + "version": "20bfd2c6+U", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "gitlab", + "appName": "git.server.net", + "domain": "git.server.net", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "0.2.2+18.3.0-ce.0", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "jitsi", + "appName": "jitsi.server.net", + "domain": "jitsi.server.net", + "status": "deployed", + "chaos": "true", + "chaosVersion": "d3dd84ab + unstaged changes", + "version": "0.0.1+8719", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "liberaforms", + "appName": "forms.test.server.net", + "domain": "forms.test.server.net", + "status": "deployed", + "chaos": "true", + "chaosVersion": "b3e74fa7+U", + "version": "b3e74fa7+U", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "mattermost", + "appName": "mattermost-demo.server.net", + "domain": "mattermost-demo.server.net", + "status": "deployed", + "chaos": "true", + "chaosVersion": "52580808", + "version": "52580808", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "openemr", + "appName": "openemr.server.net", + "domain": "openemr.server.net", + "status": "deployed", + "chaos": "true", + "chaosVersion": "6b62e331", + "version": "6b62e331", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "outline", + "appName": "wiki.server.net", + "domain": "wiki.server.net", + "status": "deployed", + "chaos": "true", + "chaosVersion": "ab70b3c4+U", + "version": "ab70b3c4+U", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "statping", + "appName": "status.server.net", + "domain": "status.server.net", + "status": "deployed", + "chaos": "true", + "chaosVersion": "bd875e08", + "version": "0.1.1+v0.90.78", + "upgrade": "latest" + }, + { + "server": "internal.server.net", + "recipe": "traefik", + "appName": "traefik.server.net", + "domain": "traefik.server.net", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "3.6.2+v3.4.5", + "upgrade": "latest" + } + ], + "appCount": 13, + "versionCount": 13, + "unversionedCount": 0, + "latestCount": 11, + "upgradeCount": 2 + }, + "internal.intranet.site.com": { + "apps": [ + { + "server": "internal.intranet.site.com", + "recipe": "backup-bot-two", + "appName": "backup.intranet.site.com", + "domain": "", + "status": "deployed", + "chaos": "false", + "chaosVersion": "2.3.0+2.3.0-beta", + "version": "2.3.0+2.3.0-beta", + "upgrade": "latest" + }, + { + "server": "internal.intranet.site.com", + "recipe": "baserow", + "appName": "base.intranet.site.com", + "domain": "base.intranet.site.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "1.1.0+1.31.1", + "upgrade": "1.2.1+1.35.3\n1.2.0+1.35.3" + }, + { + "server": "internal.intranet.site.com", + "recipe": "traefik", + "appName": "traefik.intranet.site.com", + "domain": "traefik.intranet.site.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "3.4.2+v3.4.5", + "upgrade": "3.6.2+v3.4.5\n3.6.1+v3.4.5\n3.6.0+v3.4.5\n3.5.0+v3.4.5" + }, + { + "server": "internal.intranet.site.com", + "recipe": "vaultwarden", + "appName": "pass.intranet.site.com", + "domain": "pass.intranet.site.com", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "2.0.0+1.33.2", + "upgrade": "2.1.0+1.34.1" + } + ], + "appCount": 4, + "versionCount": 4, + "unversionedCount": 0, + "latestCount": 1, + "upgradeCount": 3 + }, + "internal.test.org": { + "apps": [ + { + "server": "internal.test.org", + "recipe": "authentik", + "appName": "accounts.test.org", + "domain": "accounts.test.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "9.0.0+2025.8.1", + "upgrade": "9.0.1+2025.8.1" + }, + { + "server": "internal.test.org", + "recipe": "backup-bot-two", + "appName": "backup.test.org", + "domain": "", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "2.3.0+2.3.0-beta", + "upgrade": "latest" + }, + { + "server": "internal.test.org", + "recipe": "collabora", + "appName": "docs.test.org", + "domain": "docs.test.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "3.1.0+24.04.10.2.1", + "version": "3.1.0+24.04.10.2.1", + "upgrade": "4.0.0+25.04.4.1.1\n3.3.0+24.04.13.3.1\n3.2.0+24.04.12.3.1" + }, + { + "server": "internal.test.org", + "recipe": "custom-html", + "appName": "test.org", + "domain": "test.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "ba1c2269", + "version": "1.7.1+1.27.2", + "upgrade": "latest" + }, + { + "server": "internal.test.org", + "recipe": "garage", + "appName": "garage.test.org", + "domain": "garage.test.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "9ca66f0f", + "version": "9ca66f0f", + "upgrade": "latest" + }, + { + "server": "internal.test.org", + "recipe": "nextcloud", + "appName": "nc.test.org", + "domain": "nc.test.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "11.0.0+30.0.4-fpm", + "version": "11.0.0+30.0.4-fpm", + "upgrade": "12.0.1+31.0.6-fpm\n12.0.0+31.0.6-fpm\n11.4.0+30.0.10-fpm\n11.4.0+30.0.6-fpm\n11.3.0+30.0.6-fpm\n11.2.0+30.0.6-fpm\n11.1.0+30.0.6-fpm\n11.0.1+30.0.4-fpm" + }, + { + "server": "internal.test.org", + "recipe": "outline", + "appName": "wiki.test.org", + "domain": "wiki.test.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "2.8.1+0.81.1", + "version": "2.8.1+0.81.1", + "upgrade": "2.12.1+0.87.4\n2.12.0+0.87.3\n2.11.0+0.86.1\n2.10.0+0.84.0\n2.9.1+0.82.0\n2.9.0+0.82.0\n2.8.2+0.81.1" + }, + { + "server": "internal.test.org", + "recipe": "traefik", + "appName": "traefik.test.org", + "domain": "traefik.test.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "993ed9cf", + "version": "993ed9cf", + "upgrade": "latest" + } + ], + "appCount": 8, + "versionCount": 8, + "unversionedCount": 0, + "latestCount": 4, + "upgradeCount": 4 + }, + "orgsite.org": { + "apps": [ + { + "server": "orgsite.org", + "recipe": "authentik", + "appName": "accounts.orgsite.org", + "domain": "accounts.orgsite.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "6.11.0+2024.10.5", + "version": "6.11.0+2024.10.5", + "upgrade": "9.0.1+2025.8.1\n9.0.0+2025.8.1\n8.0.0+2025.8.1\n7.4.1+2025.6.4\n7.4.0+2025.6.3\n7.3.2+2025.6.2\n7.3.1+2025.6.1\n7.3.0+2025.6.0\n7.2.0+2025.4.1\n7.1.0+2025.2.4\n7.0.3+2025.2.3\n7.0.2+2025.2.2\n7.0.1+2025.2.0\n7.0.0+2025.2.0\n6.12.0+2024.12.3\n6.11.1+2024.10.5" + }, + { + "server": "orgsite.org", + "recipe": "backup-bot-two", + "appName": "backup.orgsite.org", + "domain": "", + "status": "deployed", + "chaos": "false", + "chaosVersion": "2.3.0+2.3.0-beta", + "version": "2.3.0+2.3.0-beta", + "upgrade": "latest" + }, + { + "server": "orgsite.org", + "recipe": "baserow", + "appName": "base.orgsite.org", + "domain": "base.orgsite.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "1.1.0+1.31.1", + "upgrade": "1.2.1+1.35.3\n1.2.0+1.35.3" + }, + { + "server": "orgsite.org", + "recipe": "collabora", + "appName": "docs.orgsite.org", + "domain": "docs.orgsite.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "3.1.0+24.04.10.2.1", + "version": "3.1.0+24.04.10.2.1", + "upgrade": "4.0.0+25.04.4.1.1\n3.3.0+24.04.13.3.1\n3.2.0+24.04.12.3.1" + }, + { + "server": "orgsite.org", + "recipe": "jitsi", + "appName": "meet.orgsite.org", + "domain": "meet.orgsite.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "d3dd84ab", + "version": "0.0.1+8719", + "upgrade": "latest" + }, + { + "server": "orgsite.org", + "recipe": "mattermost", + "appName": "chat.orgsite.org", + "domain": "chat.orgsite.org", + "status": "deployed", + "chaos": "true", + "chaosVersion": "5f0295b9", + "version": "2.1.0+10.5.0", + "upgrade": "latest" + }, + { + "server": "orgsite.org", + "recipe": "nextcloud", + "appName": "nc.orgsite.org", + "domain": "nc.orgsite.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "11.1.0+30.0.6-fpm", + "version": "11.1.0+30.0.6-fpm", + "upgrade": "12.0.1+31.0.6-fpm\n12.0.0+31.0.6-fpm\n11.4.0+30.0.10-fpm\n11.4.0+30.0.6-fpm\n11.3.0+30.0.6-fpm\n11.2.0+30.0.6-fpm" + }, + { + "server": "orgsite.org", + "recipe": "traefik", + "appName": "traefik.orgsite.org", + "domain": "traefik.orgsite.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "3.4.2+v3.4.5", + "upgrade": "3.6.2+v3.4.5\n3.6.1+v3.4.5\n3.6.0+v3.4.5\n3.5.0+v3.4.5" + }, + { + "server": "orgsite.org", + "recipe": "vaultwarden", + "appName": "pass.orgsite.org", + "domain": "pass.orgsite.org", + "status": "deployed", + "chaos": "false", + "chaosVersion": "unknown", + "version": "2.0.0+1.33.2", + "upgrade": "2.1.0+1.34.1" + } + ], + "appCount": 9, + "versionCount": 9, + "unversionedCount": 0, + "latestCount": 3, + "upgradeCount": 6 + } +} \ No newline at end of file diff --git a/web/src/services/mock-catalogue.json b/web/src/services/mock-catalogue.json new file mode 100644 index 0000000..3d11e40 --- /dev/null +++ b/web/src/services/mock-catalogue.json @@ -0,0 +1,1726 @@ +[ + { + "category": "Apps", + "default_branch": "main", + "description": "An awesome, Online Office suite image suitable for home use.", + "features": { + "backups": "No", + "email": "3", + "healthcheck": "No", + "image": { + "image": "collabora", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/collabora/code" + }, + "status": 3, + "tests": "2", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/249-0937028d43099df58d85bc41a7e69fbd", + "name": "collabora", + "repository": "https://git.coopcloud.tech/coop-cloud/collabora.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/collabora.git", + "versions": [ + { + "1.0.0+6.4.9.3": { + "app": { + "image": "collabora/code", + "tag": "6.4.9.3" + }, + "web": { + "image": "nginx", + "tag": "1.21.4" + } + } + }, + { + "2.0.0+21.11.0.5.1": { + "app": { + "image": "collabora/code", + "tag": "21.11.0.5.1" + }, + "web": { + "image": "nginx", + "tag": "1.21.4" + } + } + }, + { + "2.1.0+21.11.1.4.1": { + "app": { + "image": "collabora/code", + "tag": "21.11.1.4.1" + }, + "web": { + "image": "nginx", + "tag": "1.21.4" + } + } + }, + { + "2.1.2+22.05.8.2.1": { + "app": { + "image": "collabora/code", + "tag": "22.05.8.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.22.1" + } + } + }, + { + "2.2.0+22.05.10.1.1": { + "app": { + "image": "collabora/code", + "tag": "22.05.10.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.23.3" + } + } + }, + { + "2.3.0+22.05.14.3.1": { + "app": { + "image": "collabora/code", + "tag": "22.05.14.3.1" + }, + "web": { + "image": "nginx", + "tag": "1.24.0" + } + } + }, + { + "2.4.0+23.05.2.2.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.2.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.2" + } + } + }, + { + "2.5.0+23.05.3.1.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.3.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.2" + } + } + }, + { + "2.6.0+23.05.5.4.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.5.4.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.3" + } + } + }, + { + "2.7.0+23.05.8.2.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.8.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.4" + } + } + }, + { + "2.7.1+23.05.10.1.1": { + "app": { + "image": "collabora/code", + "tag": "23.05.10.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.25.4" + } + } + }, + { + "3.0.0+24.04.6.1.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.6.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.27.0" + } + } + }, + { + "3.1.0+24.04.10.2.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.10.2.1" + }, + "web": { + "image": "nginx", + "tag": "1.27.3" + } + } + }, + { + "3.2.0+24.04.12.3.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.12.3.1" + }, + "web": { + "image": "nginx", + "tag": "1.27.4" + } + } + }, + { + "3.3.0+24.04.13.3.1": { + "app": { + "image": "collabora/code", + "tag": "24.04.13.3.1" + }, + "web": { + "image": "nginx", + "tag": "1.28.0" + } + } + }, + { + "4.0.0+25.04.4.1.1": { + "app": { + "image": "collabora/code", + "tag": "25.04.4.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.29.0" + } + } + }, + { + "4.0.1+25.04.9.1.1": { + "app": { + "image": "collabora/code", + "tag": "25.04.9.1.1" + }, + "web": { + "image": "nginx", + "tag": "1.29.4" + } + } + }, + { + "4.0.2+25.04.9.4.1": { + "app": { + "image": "collabora/code", + "tag": "25.04.9.4.1" + }, + "web": { + "image": "nginx", + "tag": "1.29.7" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "HTTP/HTTPS compression proxy ", + "features": { + "backups": "No", + "email": "N/A", + "healthcheck": "No", + "image": { + "image": "compy", + "rating": "0", + "source": "own", + "url": "https://hub.docker.com/r/thecoopcloud/compy" + }, + "status": 1, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/435-127e3f583ac79a3b71f8fbd9c1cf5ba1", + "name": "compy", + "repository": "https://git.coopcloud.tech/coop-cloud/compy.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/compy.git", + "versions": [], + "website": "" + }, + { + "category": "Development", + "default_branch": "main", + "description": "A painless self-hosted Git service", + "features": { + "backups": "Yes", + "email": "Yes", + "healthcheck": "Yes", + "image": { + "image": "forgejo/forgejo", + "rating": "4", + "source": "upstream", + "url": "https://codeberg.org/forgejo/-/packages/container/forgejo/13-rootless" + }, + "status": 5, + "tests": "2", + "sso": "3 (OAuth)" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/eb3fb3ee823159765f746a75128143f3cd9b11c3f074ff6043ceeac8cdc434a9", + "name": "forgejo", + "repository": "https://git.coopcloud.tech/coop-cloud/forgejo.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/forgejo.git", + "versions": [ + { + "1.0.0+1.14.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.14.5-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.0+1.15.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.0-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.1+1.15.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.3-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.2+1.15.6-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.6-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.3+1.15.10-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.15.10-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.2.0+1.16.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.16.3-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.2.1+1.16.8-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.16.8-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.3.0+1.17.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.17.2-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.3.1+1.17.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.17.3-rootless" + }, + "db": { + "image": "mariadb", + "tag": "10.9" + } + } + }, + { + "2.0.0+1.18.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.18.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "9.6" + } + } + }, + { + "2.0.1+1.18.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.18.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "9.6" + } + } + }, + { + "2.1.0+1.18.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.18.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "9.6" + } + } + }, + { + "2.1.2+1.19.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.19.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.2" + } + } + }, + { + "2.2.0+1.19.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.19.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.3" + } + } + }, + { + "2.3.0+1.20.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.3" + } + } + }, + { + "2.3.1+1.20.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.3" + } + } + }, + { + "2.3.2+1.20.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.4" + } + } + }, + { + "2.3.3+1.20.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.20.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.4" + } + } + }, + { + "2.4.0+1.21.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.4" + } + } + }, + { + "2.5.0+1.21.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.5.1+1.21.4-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.4-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.5.2+1.21.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.6.0+1.21.5-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.5-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.6.1+1.21.10-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.10-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.6.2+1.21.10-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.10-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.5" + } + } + }, + { + "2.7.0+1.21.11-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.11-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.6" + } + } + }, + { + "2.8.0+1.21.11-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.21.11-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.9.0+1.22.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.9.1+1.22.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.10.0+1.22.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.7" + } + } + }, + { + "2.10.1+1.22.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "2.11.0+1.22.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "3.0.0+1.22.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "3.0.1+1.22.3-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.8" + } + } + }, + { + "3.0.2+1.22.6-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.6-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.0.3+1.22.6-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.22.6-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.1.0+1.23.0-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.0-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.1.1+1.23.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.2.0+1.23.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.3.0+1.23.1-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.3.1+1.23.8-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.23.8-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.10" + } + } + }, + { + "3.4.0+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "3.5.0+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "3.5.1+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "3.5.2+1.24.2-rootless": { + "app": { + "image": "gitea/gitea", + "tag": "1.24.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "4.0.0+12.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "12.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "4.0.1+12.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "12.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "4.0.2+12.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "12.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.0+13.0.2-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.2-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.1+13.0.3-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.2+13.0.4-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.4-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.0.3+13.0.4-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "13.0.4-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.1.0+14.0.1-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "14.0.1-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + }, + { + "5.1.1+14.0.3-rootless": { + "app": { + "image": "forgejo/forgejo", + "tag": "14.0.3-rootless" + }, + "db": { + "image": "postgres", + "tag": "15.13" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "A shared agenda for local communities", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "gancio", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/cisti/gancio" + }, + "status": 0, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/485-f4a447197439adc13c2440728f1ba8b6", + "name": "gancio", + "repository": "https://git.coopcloud.tech/coop-cloud/gancio.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/gancio.git", + "versions": [ + { + "0.1.0+1.25.1": { + "app": { + "image": "cisti/gancio", + "tag": "1.25.1" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "A federated link aggregator in Rust", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "Yes", + "image": { + "image": "lemmy", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/dessalines/lemmy" + }, + "status": 1, + "tests": "2", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/432-5751cb07a13ead1bea8249a8f3b5ae68", + "name": "lemmy", + "repository": "https://git.coopcloud.tech/coop-cloud/lemmy.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/lemmy.git", + "versions": [ + { + "0.1.0+0.16.6": { + "app": { + "image": "dessalines/lemmy", + "tag": "0.16.6" + }, + "db": { + "image": "postgres", + "tag": "12-alpine" + }, + "pictrs": { + "image": "asonix/pictrs", + "tag": "0.3.0-beta.12-r1" + }, + "ui": { + "image": "dessalines/lemmy-ui", + "tag": "0.16.6" + }, + "web": { + "image": "nginx", + "tag": "1.20.0" + } + } + }, + { + "0.1.0+0.16.1": { + "app": { + "image": "dessalines/lemmy", + "tag": "0.16.1" + }, + "db": { + "image": "postgres", + "tag": "12-alpine" + }, + "pictrs": { + "image": "asonix/pictrs", + "tag": "0.3.0-beta.12-r1" + }, + "ui": { + "image": "dessalines/lemmy-ui", + "tag": "0.16.1" + }, + "web": { + "image": "nginx", + "tag": "1.20.0" + } + } + }, + { + "0.2.0+0.18.3": { + "app": { + "image": "dessalines/lemmy", + "tag": "0.18.3" + }, + "db": { + "image": "postgres", + "tag": "15-alpine" + }, + "pictrs": { + "image": "asonix/pictrs", + "tag": "0.4.0-beta.19" + }, + "ui": { + "image": "dessalines/lemmy-ui", + "tag": "0.18.3" + }, + "web": { + "image": "nginx", + "tag": "1.20.0" + } + } + } + ], + "website": "" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Spin up an onion service for your Co-op Cloud applications", + "features": { + "backups": "Yes", + "email": "N/A", + "healthcheck": "Yes", + "image": { + "image": "onimages/tor:alpine", + "rating": "4", + "source": "upstream", + "url": "https://onionservices.torproject.org/apps/base/containers" + }, + "status": 0, + "tests": "No", + "sso": "N/A" + }, + "icon": "", + "name": "onion", + "repository": "https://git.coopcloud.tech/coop-cloud/onion.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/onion.git", + "versions": [ + { + "1.0.0+alpine": { + "app": { + "image": "tpo/onion-services/onimages/tor", + "tag": "alpine" + } + } + }, + { + "1.0.1+alpine": { + "app": { + "image": "tpo/onion-services/onimages/tor", + "tag": "alpine" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "", + "features": { + "backups": "Yes", + "email": "Yes", + "healthcheck": "3", + "image": { + "image": "vaultwarden/server", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/vaultwarden/server" + }, + "status": 2, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/379-697fbe9f0f9481f250fde86e811c1e34", + "name": "vaultwarden", + "repository": "https://git.coopcloud.tech/coop-cloud/vaultwarden.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/vaultwarden.git", + "versions": [ + { + "0.1.0+1.25.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.25.0" + } + } + }, + { + "0.1.1+1.26.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.26.0" + } + } + }, + { + "0.2.0+1.26.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.26.0" + } + } + }, + { + "0.3.0+1.26.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.26.0" + } + } + }, + { + "0.4.0+1.28.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.28.1" + } + } + }, + { + "0.5.0+1.29.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.29.0" + } + } + }, + { + "0.5.1+1.29.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.29.1" + } + } + }, + { + "0.6.0+1.29.2": { + "app": { + "image": "vaultwarden/server", + "tag": "1.29.2" + } + } + }, + { + "0.7.0+1.30.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.30.0" + } + } + }, + { + "0.7.1+1.30.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.30.1" + } + } + }, + { + "0.7.2+1.30.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.30.3" + } + } + }, + { + "0.8.0+1.31.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.31.0" + } + } + }, + { + "0.9.0+1.32.0": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.0" + } + } + }, + { + "0.9.1+1.32.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.3" + } + } + }, + { + "1.0.0+1.32.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.3" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.1+1.32.5": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.5" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.2+1.32.5": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.5" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.3+1.32.5": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.5" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.0.4+1.32.7": { + "app": { + "image": "vaultwarden/server", + "tag": "1.32.7" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "1.1.0+1.33.2": { + "app": { + "image": "vaultwarden/server", + "tag": "1.33.2" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "2.0.0+1.33.2": { + "app": { + "image": "vaultwarden/server", + "tag": "1.33.2" + }, + "db": { + "image": "mariadb", + "tag": "10.6" + } + } + }, + { + "2.1.0+1.34.1": { + "app": { + "image": "vaultwarden/server", + "tag": "1.34.1" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + }, + { + "2.1.1+1.34.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.34.3" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + }, + { + "2.1.2+1.35.3": { + "app": { + "image": "vaultwarden/server", + "tag": "1.35.3" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + }, + { + "2.1.3+1.35.4": { + "app": { + "image": "vaultwarden/server", + "tag": "1.35.4" + }, + "db": { + "image": "mariadb", + "tag": "10.11" + } + } + } + ], + "website": "" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Easily and securely send things from one computer to another", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "croc", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/schollz/croc" + }, + "status": 3, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/365-d44b58c8c77808fb82c2d9fdfee17800", + "name": "croc", + "repository": "https://git.coopcloud.tech/coop-cloud/croc.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/croc.git", + "versions": [], + "website": "https://github.com/schollz/croc" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Community Keycloak SSO user management.", + "features": { + "backups": "No", + "email": "N/A", + "healthcheck": "Yes", + "image": { + "image": "", + "rating": "", + "source": "", + "url": "" + }, + "status": 1, + "tests": "Yes", + "sso": "N/A" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/281-ae6d28ba5409a428cbb3a49d5ef6d739", + "name": "keycloak-collective-portal", + "repository": "https://git.coopcloud.tech/coop-cloud/keycloak-collective-portal.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/keycloak-collective-portal.git", + "versions": [], + "website": "" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "Manage a node in a local resilient mesh, see https://lores.tech/", + "features": { + "backups": "No", + "email": "N/A", + "healthcheck": "No", + "image": { + "image": "lores-node", + "rating": "4", + "source": "upstream", + "url": "https://ghcr.io/local-resilience-tech/lores-node:latest" + }, + "status": 1, + "tests": "0", + "sso": "N/A" + }, + "icon": "", + "name": "lores-node", + "repository": "https://git.coopcloud.tech/coop-cloud/lores-node.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/lores-node.git", + "versions": [ + { + "0.1.0+0.15.3": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.15.3" + } + } + }, + { + "0.2.0+v0.16.0": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.16.0" + } + } + }, + { + "0.2.1+v0.16.1": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.16.1" + } + } + }, + { + "0.2.2+v0.16.2": { + "app": { + "image": "local-resilience-tech/lores-node", + "tag": "v0.16.2" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "", + "features": { + "backups": "Yes", + "email": "No", + "healthcheck": "", + "image": { + "image": "plausible/analytics", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/plausible/analytics" + }, + "status": 1, + "tests": "", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/ba858ce9af5a5e69074b125fd4f99ac7b43fd1ad4fb82d926541175826037921", + "name": "plausible", + "repository": "https://git.coopcloud.tech/coop-cloud/plausible.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/plausible.git", + "versions": [ + { + "1.1.0+v1.5.1": { + "app": { + "image": "plausible/analytics", + "tag": "v1.5.1" + }, + "db": { + "image": "postgres", + "tag": "13.11-alpine" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + }, + { + "2.0.0+v1.5.1": { + "app": { + "image": "plausible/analytics", + "tag": "v1.5.1" + }, + "db": { + "image": "postgres", + "tag": "13.11" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + }, + { + "3.0.0+v2.0.0": { + "app": { + "image": "plausible/analytics", + "tag": "v2.0.0" + }, + "db": { + "image": "postgres", + "tag": "13.12" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + }, + { + "3.0.1+v2.0.0": { + "app": { + "image": "plausible/analytics", + "tag": "v2.0.0" + }, + "db": { + "image": "postgres", + "tag": "13.12" + }, + "plausible_events_db": { + "image": "clickhouse/clickhouse-server", + "tag": "23.4.2.11-alpine" + } + } + } + ], + "website": "https://github.com/plausible/analytics/" + }, + { + "category": "Utilities", + "default_branch": "main", + "description": "RSS feed for Docker Hub images", + "features": { + "backups": "?", + "email": "?", + "healthcheck": "?", + "image": { + "image": "docker-hub-rss", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/theconnman/docker-hub-rss/" + }, + "status": 0, + "tests": "?", + "sso": "?" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/210-09cd28347983de7287a1af0792371087", + "name": "docker-hub-rss", + "repository": "https://git.coopcloud.tech/coop-cloud/docker-hub-rss.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/docker-hub-rss.git", + "versions": [ + { + "0.1.0+0.4.2": { + "app": { + "image": "theconnman/docker-hub-rss", + "tag": "0.4.2" + } + } + } + ], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "A web-based farm record keeping application. ", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "farmos", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/farmos" + }, + "status": 0, + "tests": "No", + "sso": "No" + }, + "icon": "", + "name": "farmos", + "repository": "https://git.coopcloud.tech/coop-cloud/farmos.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/farmos.git", + "versions": [], + "website": "" + }, + { + "category": "Apps", + "default_branch": "main", + "description": "Funkwhale is a self-hosted, modern free and open-source music server, heavily inspired by Grooveshark.", + "features": { + "backups": "No", + "email": "No", + "healthcheck": "No", + "image": { + "image": "funkwhale", + "rating": "4", + "source": "upstream", + "url": "https://hub.docker.com/r/funkwhale" + }, + "status": 0, + "tests": "No", + "sso": "No" + }, + "icon": "https://git.coopcloud.tech/repo-avatars/455-5c7ee2a7d5ca18dbd1fba168994f9196", + "name": "funkwhale", + "repository": "https://git.coopcloud.tech/coop-cloud/funkwhale.git", + "ssh_url": "ssh://git@git.coopcloud.tech:2222/coop-cloud/funkwhale.git", + "versions": [ + { + "0.1.0+1.25.3": { + "api": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "app": { + "image": "nginx", + "tag": "1.25.3" + }, + "cache": { + "image": "redis", + "tag": "7-alpine" + }, + "celerybeat": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "celeryworker": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "db": { + "image": "postgres", + "tag": "10-alpine" + } + } + }, + { + "0.1.1+1.27.1": { + "api": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "app": { + "image": "nginx", + "tag": "1.27.1" + }, + "cache": { + "image": "redis", + "tag": "7-alpine" + }, + "celerybeat": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "celeryworker": { + "image": "funkwhale/funkwhale", + "tag": "1.2" + }, + "db": { + "image": "postgres", + "tag": "10-alpine" + } + } + } + ], + "website": "" + } +] \ No newline at end of file diff --git a/web/src/services/mock-logs.json b/web/src/services/mock-logs.json new file mode 100644 index 0000000..5c19923 --- /dev/null +++ b/web/src/services/mock-logs.json @@ -0,0 +1,326 @@ +{ + "deploy": { + "logs": [ + { + "type": "info", + "text": "📦 Deploying {appName}..." + }, + { + "type": "command", + "text": "$ abra app deploy {appName}" + }, + { + "type": "info", + "text": "Checking server connection..." + }, + { + "type": "success", + "text": "✓ Connected to server" + }, + { + "type": "info", + "text": "Pulling latest images..." + }, + { + "type": "output", + "text": "latest: Pulling from library/traefik" + }, + { + "type": "output", + "text": "a1b2c3d4: Pull complete" + }, + { + "type": "output", + "text": "e5f6g7h8: Pull complete" + }, + { + "type": "success", + "text": "✓ Images pulled successfully" + }, + { + "type": "info", + "text": "Creating containers..." + }, + { + "type": "output", + "text": "Creating {appName}_app_1 ... done" + }, + { + "type": "output", + "text": "Creating {appName}_db_1 ... done" + }, + { + "type": "info", + "text": "Starting services..." + }, + { + "type": "output", + "text": "Starting {appName}_app_1 ... done" + }, + { + "type": "output", + "text": "Starting {appName}_db_1 ... done" + }, + { + "type": "success", + "text": "✓ {appName} deployed successfully!" + } + ] + }, + "stop": { + "logs": [ + { + "type": "info", + "text": "🛑 Stopping {appName}..." + }, + { + "type": "command", + "text": "$ abra app stop {appName}" + }, + { + "type": "output", + "text": "Stopping {appName}_app_1 ... done" + }, + { + "type": "output", + "text": "Stopping {appName}_db_1 ... done" + }, + { + "type": "success", + "text": "✓ {appName} stopped" + } + ] + }, + "start": { + "logs": [ + { + "type": "info", + "text": "▶️ Starting {appName}..." + }, + { + "type": "command", + "text": "$ abra app start {appName}" + }, + { + "type": "output", + "text": "Starting {appName}_app_1 ... done" + }, + { + "type": "output", + "text": "Starting {appName}_db_1 ... done" + }, + { + "type": "success", + "text": "✓ {appName} started" + } + ] + }, + "upgrade": { + "logs": [ + { + "type": "info", + "text": "Upgrading {appName} to {version}..." + }, + { + "type": "command", + "text": "$ abra app upgrade {appName}" + }, + { + "type": "info", + "text": "Pulling new version..." + }, + { + "type": "output", + "text": "{version}: Pulling from library/app" + }, + { + "type": "output", + "text": "x9y8z7w6: Pull complete" + }, + { + "type": "success", + "text": "✓ New version pulled" + }, + { + "type": "info", + "text": "Stopping old containers..." + }, + { + "type": "output", + "text": "Stopping {appName}_app_1 ... done" + }, + { + "type": "info", + "text": "Starting new containers..." + }, + { + "type": "output", + "text": "Starting {appName}_app_1 ... done" + }, + { + "type": "success", + "text": "✓ {appName} upgraded to {version}" + } + ] + }, + "remove": { + "logs": [ + { + "type": "warning", + "text": "⚠️ Removing {appName}..." + }, + { + "type": "command", + "text": "$ abra app remove {appName}" + }, + { + "type": "output", + "text": "Stopping {appName}_app_1 ... done" + }, + { + "type": "output", + "text": "Stopping {appName}_db_1 ... done" + }, + { + "type": "output", + "text": "Removing {appName}_app_1 ... done" + }, + { + "type": "output", + "text": "Removing {appName}_db_1 ... done" + }, + { + "type": "output", + "text": "Removing network {appName}_default" + }, + { + "type": "success", + "text": "✓ {appName} removed" + } + ] + }, + "serverRefresh": { + "logs": [ + { + "type": "info", + "text": "🔄 Refreshing server {serverName}..." + }, + { + "type": "command", + "text": "$ abra server ls" + }, + { + "type": "info", + "text": "Fetching server status..." + }, + { + "type": "output", + "text": "Checking connection..." + }, + { + "type": "success", + "text": "✓ Connected" + }, + { + "type": "output", + "text": "Fetching app list..." + }, + { + "type": "success", + "text": "✓ Found apps on {serverName}" + }, + { + "type": "success", + "text": "✓ Server refreshed" + } + ] + }, + "deployAll": { + "logs": [ + { + "type": "info", + "text": "📦 Deploying all apps on {serverName}..." + }, + { + "type": "command", + "text": "$ abra app deploy --all --server {serverName}" + }, + { + "type": "info", + "text": "Found {appCount} apps to deploy" + }, + { + "type": "output", + "text": "Deploying apps in parallel..." + }, + { + "type": "info", + "text": "[1/{appCount}] Deploying app_1..." + }, + { + "type": "success", + "text": "✓ app_1 deployed" + }, + { + "type": "info", + "text": "[2/{appCount}] Deploying app_2..." + }, + { + "type": "success", + "text": "✓ app_2 deployed" + }, + { + "type": "info", + "text": "[3/{appCount}] Deploying app_3..." + }, + { + "type": "success", + "text": "✓ app_3 deployed" + }, + { + "type": "success", + "text": "✓ All apps deployed on {serverName}" + } + ] + }, + "upgradeAll": { + "logs": [ + { + "type": "info", + "text": "Upgrading all apps on {serverName}..." + }, + { + "type": "command", + "text": "$ abra app upgrade --all --server {serverName}" + }, + { + "type": "info", + "text": "Found {upgradeCount} apps with available upgrades" + }, + { + "type": "output", + "text": "Upgrading apps sequentially..." + }, + { + "type": "info", + "text": "[1/{upgradeCount}] Upgrading app_1..." + }, + { + "type": "success", + "text": "✓ app_1 upgraded" + }, + { + "type": "info", + "text": "[2/{upgradeCount}] Upgrading app_2..." + }, + { + "type": "success", + "text": "✓ app_2 upgraded" + }, + { + "type": "success", + "text": "✓ All apps upgraded on {serverName}" + } + ] + } +} \ No newline at end of file diff --git a/web/src/services/mock-servers.json b/web/src/services/mock-servers.json new file mode 100644 index 0000000..8aec675 --- /dev/null +++ b/web/src/services/mock-servers.json @@ -0,0 +1,30 @@ +[ + { + "host": "mydomain.com", + "name": "mydomain.com" + }, + { + "host": "coop.test.org", + "name": "coop.test.org" + }, + { + "host": "internal.website.com", + "name": "internal.website.com" + }, + { + "host": "internal.server.net", + "name": "internal.server.net" + }, + { + "host": "internal.intranet.site.com", + "name": "internal.intranet.site.com" + }, + { + "host": "internal.test.org", + "name": "internal.test.org" + }, + { + "host": "orgsite.org", + "name": "orgsite.org" + } +] \ No newline at end of file diff --git a/web/src/services/mockApi.ts b/web/src/services/mockApi.ts new file mode 100644 index 0000000..09a5cfb --- /dev/null +++ b/web/src/services/mockApi.ts @@ -0,0 +1,145 @@ +import type { AbraRecipe, AbraServer, ServerAppsResponse } from '../types'; +import appsData from './mock-apps.json'; +import serversData from './mock-servers.json'; +import logsData from './mock-logs.json'; +import catalogue from './mock-catalogue.json'; + + +// Log entry type +export type LogEntry = { + type: 'info' | 'success' | 'error' | 'warning' | 'command' | 'output'; + text: string; + timestamp: Date; +}; + +// Type for the imported JSON structure +type LogTemplate = { + type: 'info' | 'success' | 'error' | 'warning' | 'command' | 'output'; + text: string; +}; + +type LogsDataStructure = { + [key: string]: { + logs: LogTemplate[]; + }; +}; + +// Simulate API delay +const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +// Helper to replace template variables like {appName}, {version} +const replaceVars = (text: string, vars: Record): string => { + let result = text; + Object.entries(vars).forEach(([key, value]) => { + result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), String(value)); + }); + return result; +}; + +// Helper to process log templates into LogEntry objects +const processLogs = ( + action: string, + vars: Record +): LogEntry[] => { + console.log('Processing logs for action:', action); + console.log('Loaded logsData:', logsData); + + const typedLogsData = logsData as LogsDataStructure; + const actionData = typedLogsData[action]; + + if (!actionData || !actionData.logs) { + console.error(`No logs found for action: ${action}`); + return []; + } + + console.log('Found logs:', actionData.logs); + + const templates = actionData.logs; + return templates + .filter(template => template && template.type && template.text) // Filter out any undefined/malformed entries + .map(template => ({ + type: template.type, + text: replaceVars(template.text, vars), + timestamp: new Date(), + })); +}; + +export const mockApiService = { + async getAppsGrouped(): Promise { + await delay(500); + return appsData as ServerAppsResponse; + }, + + async getServers(): Promise { + await delay(300); + return serversData as AbraServer[]; + }, + + async deployApp(appName: string, onLog?: (log: LogEntry) => void): Promise { + const logs = processLogs('deploy', { appName }); + + for (const log of logs) { + await delay(200); + onLog?.(log); + } + }, + + async stopApp(appName: string, onLog?: (log: LogEntry) => void): Promise { + const logs = processLogs('stop', { appName }); + + for (const log of logs) { + await delay(150); + onLog?.(log); + } + }, + + async upgradeApp(appName: string, version: string, onLog?: (log: LogEntry) => void): Promise { + const logs = processLogs('upgrade', { appName, version }); + + for (const log of logs) { + await delay(200); + onLog?.(log); + } + }, + + async removeApp(appName: string, onLog?: (log: LogEntry) => void): Promise { + const logs = processLogs('remove', { appName }); + + for (const log of logs) { + await delay(180); + onLog?.(log); + } + }, + + async refreshServer(serverName: string, onLog?: (log: LogEntry) => void): Promise { + const logs = processLogs('serverRefresh', { serverName }); + + for (const log of logs) { + await delay(200); + onLog?.(log); + } + }, + + async deployAllApps(serverName: string, appCount: number, onLog?: (log: LogEntry) => void): Promise { + const logs = processLogs('deployAll', { serverName, appCount }); + + for (const log of logs) { + await delay(250); + onLog?.(log); + } + }, + + async upgradeAllApps(serverName: string, upgradeCount: number, onLog?: (log: LogEntry) => void): Promise { + const logs = processLogs('upgradeAll', { serverName, upgradeCount }); + + for (const log of logs) { + await delay(250); + onLog?.(log); + } + }, + // recipe catalog imports + async getRecipes(): Promise { + await delay(300); + return catalogue as AbraRecipe[]; + } +}; \ No newline at end of file diff --git a/web/src/types/index.ts b/web/src/types/index.ts new file mode 100644 index 0000000..b20bce4 --- /dev/null +++ b/web/src/types/index.ts @@ -0,0 +1,126 @@ +export interface ApiError { + message: string; + status: number; +} + +export interface AbraApp { + server: string; + recipe: string; + appName: string; + domain: string; + status: 'deployed' | 'stopped' | 'unknown'; + chaos: string; + chaosVersion: string; + version: string; + upgrade: string; +} + +export interface AbraServer { + name: string; + host: string; +} + +export interface ServerAppsResponse { + [serverName: string]: { + apps: AbraApp[]; + appCount: number; + versionCount: number; + unversionedCount: number; + latestCount: number; + upgradeCount: number; + backupEnabled: boolean; + }; +} + +export interface AppWithServer extends AbraApp { + serverStats: { + appCount: number; + upgradeCount: number; + }; +} + +export interface AbraAppService { + service: string; + chaos: boolean; + created: string; + image: string; + ports: string; + state: string; + status: string; + version: string; +} +export interface AbraServiceState { + name: string; + err: string; + id: string; + status: string; + retries: number; + health: string; + rollback: boolean; + failed: boolean; +} +export interface DeployState { + count: number; + total: number; + failed: boolean; + quit: boolean; +} + +export type DeployEvent = + | { + type: "service"; + data: AbraServiceState; + } + | { + type: "done"; + data: DeployState; + } +export interface Image { + image: string; + rating: string + source: string + url: string +} +type RecipeVersions = Record>[]; +export interface ServiceMeta { + image: string; + tag: string +} +export interface Features { + backups: string; + email: string; + healthcheck: string; + image: Image; + status: number; + tests: string; + sso: string; +} +export interface AbraRecipe { + category: string; + default_branch: string; + description: string; + features: Features; + icon: string; + name: string; + repository: string; + ssh_url: string; + versions: RecipeVersions; + website: string; +} + +export interface AbraAppSecret { + name: string; + version: string; + value: string; + created: boolean; +} + +export interface File { + content: string; +} + +export interface AppSnapshot { + createdAt: string; + snapshot: string; + version: string; +} \ No newline at end of file diff --git a/web/tsconfig.app.json b/web/tsconfig.app.json new file mode 100644 index 0000000..a9b5a59 --- /dev/null +++ b/web/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/web/tsconfig.node.json b/web/tsconfig.node.json new file mode 100644 index 0000000..8a67f62 --- /dev/null +++ b/web/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..3e34260 --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], +}) \ No newline at end of file -- 2.54.0