From f8e720a52f7e28263ece32939a48bb798d81cd4d Mon Sep 17 00:00:00 2001 From: mfatihakbas <119815601+mfatihakbas@users.noreply.github.com> Date: Thu, 28 Aug 2025 10:35:59 +0200 Subject: [PATCH 01/12] save --- .gitignore | 14 + app/databuk/dashboard/.gitignore | 24 + app/databuk/dashboard/README.md | 69 + app/databuk/dashboard/eslint.config.js | 23 + app/databuk/dashboard/index.html | 13 + app/databuk/dashboard/package-lock.json | 3027 +++++++++++++++++ app/databuk/dashboard/package.json | 29 + app/databuk/dashboard/public/vite.svg | 1 + app/databuk/dashboard/src/App.css | 42 + app/databuk/dashboard/src/App.tsx | 16 + app/databuk/dashboard/src/assets/react.svg | 1 + .../src/components/sidebar/Sidebar.tsx | 37 + .../src/components/sidebar/SidebarHeader.tsx | 34 + .../src/components/sidebar/TreeView.tsx | 48 + .../src/components/sidebar/WebsiteInfo.tsx | 20 + .../dashboard/src/components/sidebar/index.ts | 3 + .../src/components/sidebar/sidebar.css | 73 + app/databuk/dashboard/src/index.css | 68 + app/databuk/dashboard/src/main.tsx | 10 + app/databuk/dashboard/src/vite-env.d.ts | 1 + app/databuk/dashboard/tsconfig.app.json | 27 + app/databuk/dashboard/tsconfig.json | 7 + app/databuk/dashboard/tsconfig.node.json | 25 + app/databuk/dashboard/vite.config.ts | 7 + 24 files changed, 3619 insertions(+) create mode 100644 app/databuk/dashboard/.gitignore create mode 100644 app/databuk/dashboard/README.md create mode 100644 app/databuk/dashboard/eslint.config.js create mode 100644 app/databuk/dashboard/index.html create mode 100644 app/databuk/dashboard/package-lock.json create mode 100644 app/databuk/dashboard/package.json create mode 100644 app/databuk/dashboard/public/vite.svg create mode 100644 app/databuk/dashboard/src/App.css create mode 100644 app/databuk/dashboard/src/App.tsx create mode 100644 app/databuk/dashboard/src/assets/react.svg create mode 100644 app/databuk/dashboard/src/components/sidebar/Sidebar.tsx create mode 100644 app/databuk/dashboard/src/components/sidebar/SidebarHeader.tsx create mode 100644 app/databuk/dashboard/src/components/sidebar/TreeView.tsx create mode 100644 app/databuk/dashboard/src/components/sidebar/WebsiteInfo.tsx create mode 100644 app/databuk/dashboard/src/components/sidebar/index.ts create mode 100644 app/databuk/dashboard/src/components/sidebar/sidebar.css create mode 100644 app/databuk/dashboard/src/index.css create mode 100644 app/databuk/dashboard/src/main.tsx create mode 100644 app/databuk/dashboard/src/vite-env.d.ts create mode 100644 app/databuk/dashboard/tsconfig.app.json create mode 100644 app/databuk/dashboard/tsconfig.json create mode 100644 app/databuk/dashboard/tsconfig.node.json create mode 100644 app/databuk/dashboard/vite.config.ts diff --git a/.gitignore b/.gitignore index cc9f7ea4..733f0414 100644 --- a/.gitignore +++ b/.gitignore @@ -179,3 +179,17 @@ cython_debug/ # PyPI configuration file .pypirc + + +# Dashboard +app/databuk/dashboard/node_modules/ +app/databuk/dashboard/dist/ +app/databuk/dashboard/.vite/ +app/databuk/dashboard/.eslintcache +app/databuk/dashboard/.env +app/databuk/dashboard/.env..local +app/databuk/dashboard/npm-debug.log* +app/databuk/dashboard/yarn-error.log* +app/databuk/dashboard/pnpm-debug.log* +app/databuk/dashboard/.DS_Store +app/databuk/dashboard/Thumbs.db \ No newline at end of file diff --git a/app/databuk/dashboard/.gitignore b/app/databuk/dashboard/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/app/databuk/dashboard/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/app/databuk/dashboard/README.md b/app/databuk/dashboard/README.md new file mode 100644 index 00000000..7959ce42 --- /dev/null +++ b/app/databuk/dashboard/README.md @@ -0,0 +1,69 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/app/databuk/dashboard/eslint.config.js b/app/databuk/dashboard/eslint.config.js new file mode 100644 index 00000000..d94e7deb --- /dev/null +++ b/app/databuk/dashboard/eslint.config.js @@ -0,0 +1,23 @@ +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' +import { globalIgnores } from 'eslint/config' + +export default tseslint.config([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/app/databuk/dashboard/index.html b/app/databuk/dashboard/index.html new file mode 100644 index 00000000..e4b78eae --- /dev/null +++ b/app/databuk/dashboard/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/app/databuk/dashboard/package-lock.json b/app/databuk/dashboard/package-lock.json new file mode 100644 index 00000000..f6e7a57e --- /dev/null +++ b/app/databuk/dashboard/package-lock.json @@ -0,0 +1,3027 @@ +{ + "name": "dashboard", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dashboard", + "version": "0.0.0", + "dependencies": { + "react": "^19.1.1", + "react-dom": "^19.1.1" + }, + "devDependencies": { + "@eslint/js": "^9.33.0", + "@types/react": "^19.1.10", + "@types/react-dom": "^19.1.7", + "@vitejs/plugin-react-swc": "^4.0.0", + "eslint": "^9.33.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.39.1", + "vite": "^7.1.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/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.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "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.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "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.0", + "minimatch": "^3.1.2", + "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.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "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.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "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.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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/@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/@rolldown/pluginutils": { + "version": "1.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz", + "integrity": "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.49.0.tgz", + "integrity": "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.49.0.tgz", + "integrity": "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz", + "integrity": "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.49.0.tgz", + "integrity": "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.49.0.tgz", + "integrity": "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.49.0.tgz", + "integrity": "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.49.0.tgz", + "integrity": "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.49.0.tgz", + "integrity": "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.49.0.tgz", + "integrity": "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.49.0.tgz", + "integrity": "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.49.0.tgz", + "integrity": "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.49.0.tgz", + "integrity": "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.49.0.tgz", + "integrity": "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.49.0.tgz", + "integrity": "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.49.0.tgz", + "integrity": "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.49.0.tgz", + "integrity": "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.49.0.tgz", + "integrity": "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.49.0.tgz", + "integrity": "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.49.0.tgz", + "integrity": "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz", + "integrity": "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@swc/core": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", + "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.24" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.13.5", + "@swc/core-darwin-x64": "1.13.5", + "@swc/core-linux-arm-gnueabihf": "1.13.5", + "@swc/core-linux-arm64-gnu": "1.13.5", + "@swc/core-linux-arm64-musl": "1.13.5", + "@swc/core-linux-x64-gnu": "1.13.5", + "@swc/core-linux-x64-musl": "1.13.5", + "@swc/core-win32-arm64-msvc": "1.13.5", + "@swc/core-win32-ia32-msvc": "1.13.5", + "@swc/core-win32-x64-msvc": "1.13.5" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz", + "integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz", + "integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz", + "integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz", + "integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz", + "integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz", + "integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz", + "integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz", + "integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz", + "integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz", + "integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.24.tgz", + "integrity": "sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "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/react": { + "version": "19.1.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", + "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.8.tgz", + "integrity": "sha512-xG7xaBMJCpcK0RpN8jDbAACQo54ycO6h4dSSmgv8+fu6ZIAdANkx/WsawASUjVXYfy+J9AbUpRMNNEsXCDfDBQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.41.0.tgz", + "integrity": "sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/type-utils": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.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.41.0", + "eslint": "^8.57.0 || ^9.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.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.41.0.tgz", + "integrity": "sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.41.0.tgz", + "integrity": "sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.41.0", + "@typescript-eslint/types": "^8.41.0", + "debug": "^4.3.4" + }, + "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.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.41.0.tgz", + "integrity": "sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.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.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.41.0.tgz", + "integrity": "sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==", + "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.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.41.0.tgz", + "integrity": "sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.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", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.41.0.tgz", + "integrity": "sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==", + "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.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.41.0.tgz", + "integrity": "sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.41.0", + "@typescript-eslint/tsconfig-utils": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/visitor-keys": "8.41.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.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/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.41.0.tgz", + "integrity": "sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.41.0", + "@typescript-eslint/types": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.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", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.41.0.tgz", + "integrity": "sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.41.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-4.0.1.tgz", + "integrity": "sha512-NQhPjysi5duItyrMd5JWZFf2vNOuSMyw+EoZyTBDzk+DkfYD8WNrsUs09sELV2cr1P15nufsN25hsUBt4CKF9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.32", + "@swc/core": "^1.13.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-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/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/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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "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.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "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.2", + "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.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "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.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "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/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/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.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/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/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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-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/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-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/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/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/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "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/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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/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/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, + "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.49.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz", + "integrity": "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==", + "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.49.0", + "@rollup/rollup-android-arm64": "4.49.0", + "@rollup/rollup-darwin-arm64": "4.49.0", + "@rollup/rollup-darwin-x64": "4.49.0", + "@rollup/rollup-freebsd-arm64": "4.49.0", + "@rollup/rollup-freebsd-x64": "4.49.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.49.0", + "@rollup/rollup-linux-arm-musleabihf": "4.49.0", + "@rollup/rollup-linux-arm64-gnu": "4.49.0", + "@rollup/rollup-linux-arm64-musl": "4.49.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.49.0", + "@rollup/rollup-linux-ppc64-gnu": "4.49.0", + "@rollup/rollup-linux-riscv64-gnu": "4.49.0", + "@rollup/rollup-linux-riscv64-musl": "4.49.0", + "@rollup/rollup-linux-s390x-gnu": "4.49.0", + "@rollup/rollup-linux-x64-gnu": "4.49.0", + "@rollup/rollup-linux-x64-musl": "4.49.0", + "@rollup/rollup-win32-arm64-msvc": "4.49.0", + "@rollup/rollup-win32-ia32-msvc": "4.49.0", + "@rollup/rollup-win32-x64-msvc": "4.49.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/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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/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/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "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/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.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/tinyglobby/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/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.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "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.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.41.0.tgz", + "integrity": "sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.41.0", + "@typescript-eslint/parser": "8.41.0", + "@typescript-eslint/typescript-estree": "8.41.0", + "@typescript-eslint/utils": "8.41.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", + "typescript": ">=4.8.4 <6.0.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/vite": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", + "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" + }, + "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/vite/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/vite/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/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/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/app/databuk/dashboard/package.json b/app/databuk/dashboard/package.json new file mode 100644 index 00000000..2255bd3a --- /dev/null +++ b/app/databuk/dashboard/package.json @@ -0,0 +1,29 @@ +{ + "name": "dashboard", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.1.1", + "react-dom": "^19.1.1" + }, + "devDependencies": { + "@eslint/js": "^9.33.0", + "@types/react": "^19.1.10", + "@types/react-dom": "^19.1.7", + "@vitejs/plugin-react-swc": "^4.0.0", + "eslint": "^9.33.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.39.1", + "vite": "^7.1.2" + } +} diff --git a/app/databuk/dashboard/public/vite.svg b/app/databuk/dashboard/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/app/databuk/dashboard/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/databuk/dashboard/src/App.css b/app/databuk/dashboard/src/App.css new file mode 100644 index 00000000..b9d355df --- /dev/null +++ b/app/databuk/dashboard/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/app/databuk/dashboard/src/App.tsx b/app/databuk/dashboard/src/App.tsx new file mode 100644 index 00000000..2782044d --- /dev/null +++ b/app/databuk/dashboard/src/App.tsx @@ -0,0 +1,16 @@ +import './App.css' +import Sidebar from './components/sidebar' + +function App() { + return ( +
+ +
+

Content Area

+

This is a placeholder. We are focusing on the sidebar in checkpoint 1.

+
+
+ ) +} + +export default App diff --git a/app/databuk/dashboard/src/assets/react.svg b/app/databuk/dashboard/src/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/app/databuk/dashboard/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx new file mode 100644 index 00000000..bbbaf58c --- /dev/null +++ b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx @@ -0,0 +1,37 @@ +import React, { useState } from 'react'; +import SidebarHeader from './SidebarHeader'; +import WebsiteInfo from './WebsiteInfo'; +import TreeView from './TreeView'; +import './sidebar.css'; + +const Sidebar: React.FC = () => { + const [isCollapsed, setIsCollapsed] = useState(false); + const [isHidden, setIsHidden] = useState(false); + + if (isHidden) { + return null; + } + + return ( + + ); +}; + +export default Sidebar; + + diff --git a/app/databuk/dashboard/src/components/sidebar/SidebarHeader.tsx b/app/databuk/dashboard/src/components/sidebar/SidebarHeader.tsx new file mode 100644 index 00000000..ba8eca2e --- /dev/null +++ b/app/databuk/dashboard/src/components/sidebar/SidebarHeader.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +type Props = { + onMinimize: () => void; + onHide: () => void; +}; + +const SidebarHeader: React.FC = ({ onMinimize, onHide }) => { + return ( +
+
+ + ⛁ + +
+

ZARR FUSE

+

Data Explorer

+
+
+
+ + +
+
+ ); +}; + +export default SidebarHeader; + + diff --git a/app/databuk/dashboard/src/components/sidebar/TreeView.tsx b/app/databuk/dashboard/src/components/sidebar/TreeView.tsx new file mode 100644 index 00000000..e164a8df --- /dev/null +++ b/app/databuk/dashboard/src/components/sidebar/TreeView.tsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react'; + +const TreeView: React.FC = () => { + const [openGroups, setOpenGroups] = useState>({ + test: true, + weather: true, + }); + + const toggle = (key: 'test' | 'weather') => + setOpenGroups((s) => ({ ...s, [key]: !s[key] })); + + return ( + + ); +}; + +export default TreeView; + + diff --git a/app/databuk/dashboard/src/components/sidebar/WebsiteInfo.tsx b/app/databuk/dashboard/src/components/sidebar/WebsiteInfo.tsx new file mode 100644 index 00000000..f5fa875d --- /dev/null +++ b/app/databuk/dashboard/src/components/sidebar/WebsiteInfo.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +const WebsiteInfo: React.FC = () => { + return ( +
+
+ 🌐 + data-explorer.local +
+
+ 📅 + v1.0.0 +
+
+ ); +}; + +export default WebsiteInfo; + + diff --git a/app/databuk/dashboard/src/components/sidebar/index.ts b/app/databuk/dashboard/src/components/sidebar/index.ts new file mode 100644 index 00000000..1ab0b443 --- /dev/null +++ b/app/databuk/dashboard/src/components/sidebar/index.ts @@ -0,0 +1,3 @@ +export { default } from './Sidebar'; + + diff --git a/app/databuk/dashboard/src/components/sidebar/sidebar.css b/app/databuk/dashboard/src/components/sidebar/sidebar.css new file mode 100644 index 00000000..5722d7a5 --- /dev/null +++ b/app/databuk/dashboard/src/components/sidebar/sidebar.css @@ -0,0 +1,73 @@ +:root { + --zf-bg: #ffffff; + --zf-fg: #111827; + --zf-fg-muted: #374151; + --zf-border: #e5e7eb; + --zf-accent: #2563eb; + --zf-hover: #f3f4f6; + --zf-sidebar-w: 260px; + --zf-sidebar-w-collapsed: 60px; +} + +.zf-sidebar { + width: var(--zf-sidebar-w); + height: 100vh; + background: var(--zf-bg); + border-right: 1px solid var(--zf-border); + display: flex; + flex-direction: column; + transition: width 200ms ease; +} + +.zf-sidebar--collapsed { + width: var(--zf-sidebar-w-collapsed); +} + +.zf-sidebar__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px; + border-bottom: 1px solid var(--zf-border); +} + +.zf-sidebar__brand { + display: flex; + align-items: center; + gap: 8px; +} + +.zf-logo { font-size: 16px; } +.zf-title { font-size: 16px; margin: 0; color: var(--zf-fg); } +.zf-subtitle { font-size: 12px; margin: 0; color: var(--zf-fg-muted); } + +.zf-sidebar__controls { display: flex; gap: 6px; } +.zf-btn { border: 1px solid var(--zf-border); background: var(--zf-bg); color: var(--zf-fg); padding: 2px 6px; border-radius: 6px; cursor: pointer; } +.zf-btn--icon { width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; } +.zf-btn:hover { background: var(--zf-hover); } + +.zf-sidebar__section { padding: 12px; } +.zf-sidebar__divider { height: 1px; background: var(--zf-border); } + +.zf-website-info { display: grid; gap: 6px; } +.zf-info-item { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--zf-fg-muted); } +.zf-info-icon { width: 16px; text-align: center; } + +.zf-tree { display: block; } +.zf-section-title { font-size: 12px; color: var(--zf-fg-muted); margin-bottom: 8px; } +.zf-tree__group { margin-bottom: 6px; } +.zf-tree__group-btn { width: 100%; display: flex; align-items: center; gap: 8px; padding: 6px 8px; border: none; background: transparent; cursor: pointer; border-radius: 6px; text-align: left; } +.zf-tree__group-btn:hover { background: var(--zf-hover); } +.zf-caret { width: 14px; text-align: center; color: var(--zf-fg-muted); } +.zf-tree__label { flex: 1; text-align: left; color: var(--zf-fg); font-size: 14px; } +.zf-badge { background: #eef2ff; color: #4338ca; font-size: 11px; padding: 2px 6px; border-radius: 999px; } +.zf-tree__icon { width: 16px; text-align: center; } +.zf-tree__list { list-style: none; margin: 4px 0 8px 22px; padding: 0; } +.zf-tree__item { padding: 4px 6px; border-radius: 6px; font-size: 13px; color: var(--zf-fg-muted); display: flex; align-items: center; gap: 8px; } +.zf-tree__item:hover { background: var(--zf-hover); color: var(--zf-fg); } + +@media (max-width: 640px) { + .zf-sidebar { position: fixed; z-index: 40; top: 0; left: 0; width: 80%; max-width: 320px; height: 100dvh; box-shadow: 0 10px 30px rgba(0,0,0,0.08); } +} + + diff --git a/app/databuk/dashboard/src/index.css b/app/databuk/dashboard/src/index.css new file mode 100644 index 00000000..08a3ac9e --- /dev/null +++ b/app/databuk/dashboard/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/app/databuk/dashboard/src/main.tsx b/app/databuk/dashboard/src/main.tsx new file mode 100644 index 00000000..bef5202a --- /dev/null +++ b/app/databuk/dashboard/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/app/databuk/dashboard/src/vite-env.d.ts b/app/databuk/dashboard/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/app/databuk/dashboard/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/app/databuk/dashboard/tsconfig.app.json b/app/databuk/dashboard/tsconfig.app.json new file mode 100644 index 00000000..227a6c67 --- /dev/null +++ b/app/databuk/dashboard/tsconfig.app.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "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/app/databuk/dashboard/tsconfig.json b/app/databuk/dashboard/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/app/databuk/dashboard/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/app/databuk/dashboard/tsconfig.node.json b/app/databuk/dashboard/tsconfig.node.json new file mode 100644 index 00000000..f85a3990 --- /dev/null +++ b/app/databuk/dashboard/tsconfig.node.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "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/app/databuk/dashboard/vite.config.ts b/app/databuk/dashboard/vite.config.ts new file mode 100644 index 00000000..2328e170 --- /dev/null +++ b/app/databuk/dashboard/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react-swc' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) From f582a40bf05d3a8c348f7947043697546d1510de Mon Sep 17 00:00:00 2001 From: mfatihakbas <119815601+mfatihakbas@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:47:03 +0200 Subject: [PATCH 02/12] feat: implement responsive sidebar with tree view and improved UI - Add collapsible sidebar with smooth transitions - Implement hierarchical tree view with folder/file icons - Add responsive design for collapsed/expanded states - Add custom scrollbars and improved button interactions - Fix TailwindCSS configuration and remove unused packages - Consolidate .gitignore files - Add comprehensive README with setup instructions Dashboard now has a fully functional sidebar with: - Expandable tree structure - Collapsible sidebar with icon-only view - Modern gradient header design - Responsive layout adjustments --- .gitignore | 8 +- app/databuk/dashboard/README.md | 87 +- app/databuk/dashboard/package-lock.json | 1168 ++++++++++++++++- app/databuk/dashboard/package.json | 20 +- app/databuk/dashboard/postcss.config.cjs | 6 + app/databuk/dashboard/src/App.css | 42 - app/databuk/dashboard/src/App.tsx | 46 +- .../src/components/sidebar/Sidebar.tsx | 157 ++- .../src/components/sidebar/SidebarHeader.tsx | 34 - .../src/components/sidebar/TreeView.tsx | 146 ++- .../src/components/sidebar/WebsiteInfo.tsx | 20 - .../src/components/sidebar/data/mockData.ts | 73 ++ .../components/sidebar/hooks/useSidebar.ts | 18 + .../dashboard/src/components/sidebar/index.ts | 5 +- .../src/components/sidebar/sidebar.css | 73 -- .../src/components/sidebar/types/sidebar.ts | 17 + app/databuk/dashboard/src/index.css | 94 +- app/databuk/dashboard/tailwind.config.js | 11 + 18 files changed, 1657 insertions(+), 368 deletions(-) create mode 100644 app/databuk/dashboard/postcss.config.cjs delete mode 100644 app/databuk/dashboard/src/App.css delete mode 100644 app/databuk/dashboard/src/components/sidebar/SidebarHeader.tsx delete mode 100644 app/databuk/dashboard/src/components/sidebar/WebsiteInfo.tsx create mode 100644 app/databuk/dashboard/src/components/sidebar/data/mockData.ts create mode 100644 app/databuk/dashboard/src/components/sidebar/hooks/useSidebar.ts delete mode 100644 app/databuk/dashboard/src/components/sidebar/sidebar.css create mode 100644 app/databuk/dashboard/src/components/sidebar/types/sidebar.ts create mode 100644 app/databuk/dashboard/tailwind.config.js diff --git a/.gitignore b/.gitignore index 733f0414..c6a62489 100644 --- a/.gitignore +++ b/.gitignore @@ -187,9 +187,13 @@ app/databuk/dashboard/dist/ app/databuk/dashboard/.vite/ app/databuk/dashboard/.eslintcache app/databuk/dashboard/.env -app/databuk/dashboard/.env..local +app/databuk/dashboard/.env.local app/databuk/dashboard/npm-debug.log* app/databuk/dashboard/yarn-error.log* app/databuk/dashboard/pnpm-debug.log* app/databuk/dashboard/.DS_Store -app/databuk/dashboard/Thumbs.db \ No newline at end of file +app/databuk/dashboard/Thumbs.db +app/databuk/dashboard/dist-ssr/ +app/databuk/dashboard/*.local +app/databuk/dashboard/.vscode/* +!app/databuk/dashboard/.vscode/extensions.json \ No newline at end of file diff --git a/app/databuk/dashboard/README.md b/app/databuk/dashboard/README.md index 7959ce42..484edb9d 100644 --- a/app/databuk/dashboard/README.md +++ b/app/databuk/dashboard/README.md @@ -1,69 +1,32 @@ -# React + TypeScript + Vite +# Dashboard - Setup and Run -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default tseslint.config([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - - // Remove tseslint.configs.recommended and replace with this - ...tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - ...tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - ...tseslint.configs.stylisticTypeChecked, +## Install +``` +cd app/databuk/dashboard +npm install +``` - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) +## Development ``` +npm run dev +``` +The app starts on http://localhost:5173 (or next free port shown in the terminal). -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: +## Build +``` +npm run build +``` +Output is written to `dist/`. -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' +## Preview production build +``` +npm run preview +``` -export default tseslint.config([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) +## Lint +``` +npm run lint ``` + +## Notes +- Env files like `.env.local` are ignored by git. diff --git a/app/databuk/dashboard/package-lock.json b/app/databuk/dashboard/package-lock.json index f6e7a57e..1eef209a 100644 --- a/app/databuk/dashboard/package-lock.json +++ b/app/databuk/dashboard/package-lock.json @@ -8,21 +8,37 @@ "name": "dashboard", "version": "0.0.0", "dependencies": { + "autoprefixer": "^10.4.21", + "lucide-react": "^0.542.0", + "postcss": "^8.5.6", "react": "^19.1.1", - "react-dom": "^19.1.1" + "react-dom": "^19.1.1", + "tailwindcss": "^3.4.17" }, "devDependencies": { - "@eslint/js": "^9.33.0", - "@types/react": "^19.1.10", - "@types/react-dom": "^19.1.7", - "@vitejs/plugin-react-swc": "^4.0.0", - "eslint": "^9.33.0", + "@eslint/js": "^9.34.0", + "@types/react": "^19.1.12", + "@types/react-dom": "^19.1.8", + "@vitejs/plugin-react-swc": "^4.0.1", + "eslint": "^9.34.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "typescript": "~5.8.3", - "typescript-eslint": "^8.39.1", - "vite": "^7.1.2" + "typescript-eslint": "^8.41.0", + "vite": "^7.1.3" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@esbuild/aix-ppc64": { @@ -687,11 +703,62 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "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", @@ -705,7 +772,6 @@ "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" @@ -715,7 +781,6 @@ "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", @@ -725,6 +790,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.32", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz", @@ -1587,11 +1662,22 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1603,6 +1689,31 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1610,13 +1721,61 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1632,7 +1791,6 @@ "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" @@ -1641,6 +1799,38 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1651,6 +1841,35 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001737", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", + "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", @@ -1668,11 +1887,46 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/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" @@ -1685,9 +1939,17 @@ "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/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1699,7 +1961,6 @@ "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", @@ -1710,6 +1971,18 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -1742,6 +2015,36 @@ "dev": true, "license": "MIT" }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.211", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz", + "integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", @@ -1784,6 +2087,15 @@ "@esbuild/win32-x64": "0.25.9" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1986,7 +2298,6 @@ "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", @@ -2003,7 +2314,6 @@ "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" @@ -2030,7 +2340,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -2053,7 +2362,6 @@ "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" @@ -2100,11 +2408,39 @@ "dev": true, "license": "ISC" }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/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, @@ -2115,11 +2451,39 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/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" @@ -2128,6 +2492,30 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "16.3.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", @@ -2158,6 +2546,18 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2195,21 +2595,55 @@ "node": ">=0.8.19" } }, - "node_modules/is-extglob": { - "version": "2.1.1", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -2222,7 +2656,6 @@ "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" @@ -2232,9 +2665,32 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2293,6 +2749,24 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2316,11 +2790,25 @@ "dev": true, "license": "MIT" }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/lucide-react": { + "version": "0.542.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz", + "integrity": "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "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" @@ -2330,7 +2818,6 @@ "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", @@ -2353,6 +2840,15 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2360,11 +2856,21 @@ "dev": true, "license": "MIT" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -2386,6 +2892,48 @@ "dev": true, "license": "MIT" }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2436,6 +2984,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2463,24 +3017,43 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/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": "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" @@ -2489,11 +3062,28 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2518,6 +3108,121 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2542,7 +3247,6 @@ "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", @@ -2580,6 +3284,47 @@ "react": "^19.1.1" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2594,7 +3339,6 @@ "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", @@ -2645,7 +3389,6 @@ "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", @@ -2688,7 +3431,6 @@ "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" @@ -2701,22 +3443,128 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/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": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "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", @@ -2730,6 +3578,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2743,6 +3613,76 @@ "node": ">=8" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", @@ -2795,7 +3735,6 @@ "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" @@ -2817,6 +3756,12 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2868,6 +3813,36 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2878,6 +3853,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/vite": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", @@ -2988,7 +3969,6 @@ "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" @@ -3010,6 +3990,106 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/app/databuk/dashboard/package.json b/app/databuk/dashboard/package.json index 2255bd3a..eceb1267 100644 --- a/app/databuk/dashboard/package.json +++ b/app/databuk/dashboard/package.json @@ -10,20 +10,24 @@ "preview": "vite preview" }, "dependencies": { + "autoprefixer": "^10.4.21", + "lucide-react": "^0.542.0", + "postcss": "^8.5.6", "react": "^19.1.1", - "react-dom": "^19.1.1" + "react-dom": "^19.1.1", + "tailwindcss": "^3.4.17" }, "devDependencies": { - "@eslint/js": "^9.33.0", - "@types/react": "^19.1.10", - "@types/react-dom": "^19.1.7", - "@vitejs/plugin-react-swc": "^4.0.0", - "eslint": "^9.33.0", + "@eslint/js": "^9.34.0", + "@types/react": "^19.1.12", + "@types/react-dom": "^19.1.8", + "@vitejs/plugin-react-swc": "^4.0.1", + "eslint": "^9.34.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.3.0", "typescript": "~5.8.3", - "typescript-eslint": "^8.39.1", - "vite": "^7.1.2" + "typescript-eslint": "^8.41.0", + "vite": "^7.1.3" } } diff --git a/app/databuk/dashboard/postcss.config.cjs b/app/databuk/dashboard/postcss.config.cjs new file mode 100644 index 00000000..33ad091d --- /dev/null +++ b/app/databuk/dashboard/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/app/databuk/dashboard/src/App.css b/app/databuk/dashboard/src/App.css deleted file mode 100644 index b9d355df..00000000 --- a/app/databuk/dashboard/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/app/databuk/dashboard/src/App.tsx b/app/databuk/dashboard/src/App.tsx index 2782044d..e5714cde 100644 --- a/app/databuk/dashboard/src/App.tsx +++ b/app/databuk/dashboard/src/App.tsx @@ -1,16 +1,44 @@ -import './App.css' -import Sidebar from './components/sidebar' +import React, { useState } from 'react'; +import { Sidebar } from './components/sidebar'; +import { Database } from 'lucide-react'; function App() { + const [isCollapsed, setIsCollapsed] = useState(false); + const [isVisible, setIsVisible] = useState(true); + return ( -
- -
-

Content Area

-

This is a placeholder. We are focusing on the sidebar in checkpoint 1.

+
+ {/* Global opener when sidebar is hidden (same look/position as header icon) */} + {!isVisible && ( + + )} + + {isVisible && ( + setIsCollapsed(!isCollapsed)} + onClose={() => setIsVisible(false)} + /> + )} +
+
+
+

Content Area

+

This is a placeholder. We are focusing on the sidebar in checkpoint 1.

+
+
- ) + ); } -export default App +export default App; diff --git a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx index bbbaf58c..2b6ec40b 100644 --- a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx +++ b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx @@ -1,32 +1,139 @@ -import React, { useState } from 'react'; -import SidebarHeader from './SidebarHeader'; -import WebsiteInfo from './WebsiteInfo'; -import TreeView from './TreeView'; -import './sidebar.css'; +import React from 'react'; +import { ChevronLeft, ChevronRight, X, Database, BarChart3, Globe} from 'lucide-react'; -const Sidebar: React.FC = () => { - const [isCollapsed, setIsCollapsed] = useState(false); - const [isHidden, setIsHidden] = useState(false); - - if (isHidden) { - return null; - } +import { TreeView } from './TreeView'; +import { mockTreeData } from './data/mockData'; +import type { SidebarProps } from './types/sidebar'; +const Sidebar: React.FC = ({ isCollapsed, onToggle, onClose }) => { return ( -
+ + {/* Log Panel */} + setShowLogPanel(false)} + />
); } diff --git a/app/databuk/dashboard/src/components/LogPanel.tsx b/app/databuk/dashboard/src/components/LogPanel.tsx new file mode 100644 index 00000000..d7cceaed --- /dev/null +++ b/app/databuk/dashboard/src/components/LogPanel.tsx @@ -0,0 +1,219 @@ +import React, { useState, useEffect } from 'react'; +import { X, AlertCircle, CheckCircle, Clock, RefreshCw } from 'lucide-react'; + +interface LogEntry { + id: string; + timestamp: string; + level: 'info' | 'warning' | 'error'; + category: 'backend' | 'store-data'; + message: string; + resolved?: boolean; +} + +interface LogPanelProps { + show: boolean; + onClose: () => void; +} + +const LogPanel: React.FC = ({ show, onClose }) => { + const [logs, setLogs] = useState([]); + const [loading, setLoading] = useState(false); + const [filter, setFilter] = useState<'all' | 'backend' | 'store-data'>('all'); + + // Mock data for now - will be replaced with API call + const mockLogs: LogEntry[] = [ + { + id: '1', + timestamp: new Date().toISOString(), + level: 'info', + category: 'backend', + message: 'S3 connection established successfully', + resolved: true + }, + { + id: '2', + timestamp: new Date(Date.now() - 60000).toISOString(), + level: 'warning', + category: 'store-data', + message: 'Variable "temperature" contains NaN values in array slice [100:200]', + resolved: false + }, + { + id: '3', + timestamp: new Date(Date.now() - 120000).toISOString(), + level: 'error', + category: 'backend', + message: 'Failed to connect to S3 endpoint - retrying...', + resolved: true + } + ]; + + useEffect(() => { + if (show) { + setLoading(true); + // Simulate API call + setTimeout(() => { + setLogs(mockLogs); + setLoading(false); + }, 500); + } + }, [show]); + + const filteredLogs = logs.filter(log => + filter === 'all' || log.category === filter + ); + + const handleResolve = (logId: string) => { + setLogs(prev => prev.map(log => + log.id === logId ? { ...log, resolved: true } : log + )); + }; + + const getLevelIcon = (level: string) => { + switch (level) { + case 'error': + return ; + case 'warning': + return ; + default: + return ; + } + }; + + const getLevelColor = (level: string) => { + switch (level) { + case 'error': + return 'border-red-200 bg-red-50'; + case 'warning': + return 'border-yellow-200 bg-yellow-50'; + default: + return 'border-green-200 bg-green-50'; + } + }; + + if (!show) return null; + + return ( +
+ {/* Overlay */} +
+ + {/* Log Panel */} +
+ {/* Header */} +
+
+
+
+ +
+
+

Store Logs

+

System and data monitoring

+
+
+ +
+ + {/* Filter Tabs */} +
+ {['all', 'backend', 'store-data'].map((filterType) => ( + + ))} +
+
+ + {/* Content */} +
+ {loading ? ( +
+
+ +

Loading logs...

+
+
+ ) : ( +
+ {filteredLogs.length === 0 ? ( +
+ +

No logs found for selected filter

+
+ ) : ( + filteredLogs.map((log) => ( +
+
+
+ {getLevelIcon(log.level)} +
+
+ + {log.category} + + + {new Date(log.timestamp).toLocaleString()} + + {log.resolved && ( + + RESOLVED + + )} +
+

{log.message}

+
+
+ + {!log.resolved && log.category === 'store-data' && ( + + )} +
+
+ )) + )} +
+ )} +
+ + {/* Footer Stats */} +
+
+ Total: {logs.length} logs + Unresolved: {logs.filter(l => !l.resolved && l.category === 'store-data').length} +
+
+
+
+ ); +}; + +export default LogPanel; diff --git a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx index a7bec791..fa8c3ede 100644 --- a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx +++ b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx @@ -28,7 +28,8 @@ const Sidebar: React.FC = ({ configData, configLoading, configError, - onNodeClick + onNodeClick, + onLogClick }) => { // S3 data state @@ -316,7 +317,10 @@ const Sidebar: React.FC = ({
{/* Fixed Footer - Store Log */}
-
+
Store Log
diff --git a/app/databuk/dashboard/src/components/sidebar/types/sidebar.ts b/app/databuk/dashboard/src/components/sidebar/types/sidebar.ts index 6ecad70e..c3b29555 100644 --- a/app/databuk/dashboard/src/components/sidebar/types/sidebar.ts +++ b/app/databuk/dashboard/src/components/sidebar/types/sidebar.ts @@ -22,5 +22,6 @@ export interface SidebarProps { configLoading: boolean; configError: string | null; onNodeClick?: (storeName: string, nodePath: string) => void; + onLogClick?: () => void; } From 60ebcd4b575b6987f219b313b9d50a74c810f5e6 Mon Sep 17 00:00:00 2001 From: mfatihakbas <119815601+mfatihakbas@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:35:05 +0200 Subject: [PATCH 08/12] Logs: add backend-only logs endpoint and UI; remove filters and mocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend Add routers/logs.py with GET /api/logs?limit= returning only error/warning entries from latest file in log_store.zarr/logs. Add LOGS_DIR to core/config.py. Wire router in backend/main.py. Frontend Update LogPanel.tsx to fetch from /api/logs, remove All/Store filters and mock data, show “Backend” badge and “No logs yet” empty state. Rename sidebar footer label from “Store Log” to “Logs”. Notes: Displays latest error/warning lines; create or clear .log files under log_store.zarr/logs to control output. No breaking changes; existing S3 endpoints unaffected. --- app/databuk/dashboard/backend/core/config.py | 1 + app/databuk/dashboard/backend/main.py | 2 + .../dashboard/backend/routers/__init__.py | 1 + app/databuk/dashboard/backend/routers/logs.py | 67 +++++++++++++ .../dashboard/src/components/LogPanel.tsx | 93 ++++++------------- .../src/components/sidebar/Sidebar.tsx | 4 +- 6 files changed, 103 insertions(+), 65 deletions(-) create mode 100644 app/databuk/dashboard/backend/routers/logs.py diff --git a/app/databuk/dashboard/backend/core/config.py b/app/databuk/dashboard/backend/core/config.py index 4a94d948..d2c773c3 100644 --- a/app/databuk/dashboard/backend/core/config.py +++ b/app/databuk/dashboard/backend/core/config.py @@ -10,6 +10,7 @@ class Settings: # Need to go up to: zarr_fuse/ (project root) PROJECT_ROOT = Path(__file__).parent.parent.parent.parent.parent.parent TEST_STORES_DIR = PROJECT_ROOT / "zarr_fuse" / "test" / "workdir" + LOGS_DIR = PROJECT_ROOT / "log_store.zarr" / "logs" # API settings API_V1_STR: str = "/api" diff --git a/app/databuk/dashboard/backend/main.py b/app/databuk/dashboard/backend/main.py index 7d6d4288..c6660a35 100644 --- a/app/databuk/dashboard/backend/main.py +++ b/app/databuk/dashboard/backend/main.py @@ -19,6 +19,7 @@ # Use absolute imports from core.config import settings from routers import config, s3 +from routers import logs @asynccontextmanager async def lifespan(app: FastAPI): @@ -52,6 +53,7 @@ async def lifespan(app: FastAPI): # Include routers app.include_router(config.router, prefix=settings.API_V1_STR) app.include_router(s3.router, prefix=settings.API_V1_STR) +app.include_router(logs.router, prefix=settings.API_V1_STR) @app.get("/") async def root(): diff --git a/app/databuk/dashboard/backend/routers/__init__.py b/app/databuk/dashboard/backend/routers/__init__.py index 873f7bbb..122f9038 100644 --- a/app/databuk/dashboard/backend/routers/__init__.py +++ b/app/databuk/dashboard/backend/routers/__init__.py @@ -1 +1,2 @@ # Routers package +from . import config, s3, logs # noqa: F401 diff --git a/app/databuk/dashboard/backend/routers/logs.py b/app/databuk/dashboard/backend/routers/logs.py new file mode 100644 index 00000000..2c6aebd0 --- /dev/null +++ b/app/databuk/dashboard/backend/routers/logs.py @@ -0,0 +1,67 @@ +from fastapi import APIRouter, HTTPException, Query +from typing import List, Dict, Any +from pathlib import Path +from core.config import settings + +router = APIRouter(prefix="/logs", tags=["logs"]) + + +def _find_latest_log_file(logs_dir: Path) -> Path | None: + if not logs_dir.exists() or not logs_dir.is_dir(): + return None + log_files = sorted(logs_dir.glob("*.log"), reverse=True) + return log_files[0] if log_files else None + + +def _parse_log_line(line: str) -> Dict[str, Any] | None: + # Very simple parser expecting a format like: + # 2025-07-31T12:34:56 level=ERROR message=Something happened + # Adjust as needed to your real log format + line = line.strip() + if not line: + return None + try: + parts = line.split(" ") + timestamp = parts[0] + remainder = " ".join(parts[1:]) + level = "info" + message = remainder + # naive extraction + if "level=" in remainder: + for token in remainder.split(): + if token.lower().startswith("level="): + level = token.split("=", 1)[1].lower() + break + if "message=" in remainder: + message = remainder.split("message=", 1)[1] + return { + "id": f"{timestamp}-{abs(hash(line))}", + "timestamp": timestamp, + "level": "error" if level == "error" else ("warning" if level in ("warn", "warning") else "info"), + "category": "backend", + "message": message, + } + except Exception: + return None + + +@router.get("") +async def get_logs(limit: int = Query(200, ge=1, le=1000)) -> Dict[str, Any]: + """Return recent backend logs filtered to errors and warnings only.""" + try: + latest = _find_latest_log_file(settings.LOGS_DIR) + if latest is None: + return {"status": "success", "logs": []} + + logs: List[Dict[str, Any]] = [] + with latest.open("r", encoding="utf-8", errors="ignore") as f: + for line in f.readlines()[-limit:]: + parsed = _parse_log_line(line) + if parsed and parsed["level"] in ("error", "warning"): + logs.append(parsed) + + return {"status": "success", "logs": logs} + except Exception as e: + raise HTTPException(status_code=500, detail=f"Failed to read logs: {str(e)}") + + diff --git a/app/databuk/dashboard/src/components/LogPanel.tsx b/app/databuk/dashboard/src/components/LogPanel.tsx index d7cceaed..f7e93562 100644 --- a/app/databuk/dashboard/src/components/LogPanel.tsx +++ b/app/databuk/dashboard/src/components/LogPanel.tsx @@ -5,7 +5,7 @@ interface LogEntry { id: string; timestamp: string; level: 'info' | 'warning' | 'error'; - category: 'backend' | 'store-data'; + category: 'backend'; message: string; resolved?: boolean; } @@ -18,50 +18,38 @@ interface LogPanelProps { const LogPanel: React.FC = ({ show, onClose }) => { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(false); - const [filter, setFilter] = useState<'all' | 'backend' | 'store-data'>('all'); + const [filter, setFilter] = useState<'backend'>('backend'); - // Mock data for now - will be replaced with API call - const mockLogs: LogEntry[] = [ - { - id: '1', - timestamp: new Date().toISOString(), - level: 'info', - category: 'backend', - message: 'S3 connection established successfully', - resolved: true - }, - { - id: '2', - timestamp: new Date(Date.now() - 60000).toISOString(), - level: 'warning', - category: 'store-data', - message: 'Variable "temperature" contains NaN values in array slice [100:200]', - resolved: false - }, - { - id: '3', - timestamp: new Date(Date.now() - 120000).toISOString(), - level: 'error', - category: 'backend', - message: 'Failed to connect to S3 endpoint - retrying...', - resolved: true - } - ]; + // No mock data; will be populated from API later useEffect(() => { if (show) { setLoading(true); - // Simulate API call - setTimeout(() => { - setLogs(mockLogs); - setLoading(false); - }, 500); + // Fetch logs (errors and warnings only) from backend + fetch('http://localhost:8000/api/logs') + .then(async (res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const data = await res.json(); + const items = Array.isArray(data.logs) ? data.logs : []; + // ensure category is 'backend' and level is either 'error' or 'warning' + const normalized = items + .filter((l: any) => l && (l.level === 'error' || l.level === 'warning')) + .map((l: any) => ({ + id: String(l.id ?? `${l.timestamp}-${Math.random()}`), + timestamp: String(l.timestamp ?? new Date().toISOString()), + level: l.level as 'error' | 'warning' | 'info', + category: 'backend' as const, + message: String(l.message ?? ''), + resolved: Boolean(l.resolved ?? false) + })); + setLogs(normalized); + }) + .catch(() => setLogs([])) + .finally(() => setLoading(false)); } }, [show]); - const filteredLogs = logs.filter(log => - filter === 'all' || log.category === filter - ); + const filteredLogs = logs; const handleResolve = (logId: string) => { setLogs(prev => prev.map(log => @@ -111,7 +99,7 @@ const LogPanel: React.FC = ({ show, onClose }) => {
-

Store Logs

+

Logs

System and data monitoring

@@ -124,22 +112,9 @@ const LogPanel: React.FC = ({ show, onClose }) => { - {/* Filter Tabs */} + {/* Backend-only badge (keeps previous tab look without interactivity) */}
- {['all', 'backend', 'store-data'].map((filterType) => ( - - ))} + Backend
@@ -157,7 +132,7 @@ const LogPanel: React.FC = ({ show, onClose }) => { {filteredLogs.length === 0 ? (
-

No logs found for selected filter

+

No logs yet

) : ( filteredLogs.map((log) => ( @@ -188,14 +163,7 @@ const LogPanel: React.FC = ({ show, onClose }) => { - {!log.resolved && log.category === 'store-data' && ( - - )} + {/* No resolve action for backend-only logs */} )) @@ -208,7 +176,6 @@ const LogPanel: React.FC = ({ show, onClose }) => {
Total: {logs.length} logs - Unresolved: {logs.filter(l => !l.resolved && l.category === 'store-data').length}
diff --git a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx index fa8c3ede..16d28c06 100644 --- a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx +++ b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx @@ -315,14 +315,14 @@ const Sidebar: React.FC = ({ )} - {/* Fixed Footer - Store Log */} + {/* Fixed Footer - Logs */}
- Store Log + Logs
From 6f734a6ac2a3e2ad0e0b214d9dce9849a627b6ab Mon Sep 17 00:00:00 2001 From: mfatihakbas <119815601+mfatihakbas@users.noreply.github.com> Date: Mon, 15 Sep 2025 17:06:24 +0200 Subject: [PATCH 09/12] Modularize backend env + docs; remove legacy requirements; fix env loading pathModularize backend env + docs; remove legacy requirements; fix env loading path Description: Backend now loads environment only from app/databuk/dashboard/backend/.env (no root fallback) Fixed loader path in core/config.py to point to backend directory Removed hardcoded root .env loading from backend/main.py Deleted redundant backend/requirements.txt (using pyproject.toml) Updated app/databuk/dashboard/backend/README.md with new setup, endpoints, and env instructions Updated app/databuk/dashboard/README.md to reflect backend packaging, .env location, and run commands Minor docs cleanup and endpoint listings (s3, logs, config) --- app/databuk/dashboard/README.md | 54 +++++------ app/databuk/dashboard/backend/.gitignore | 6 ++ app/databuk/dashboard/backend/README.md | 96 +++++++++++-------- app/databuk/dashboard/backend/core/config.py | 9 ++ app/databuk/dashboard/backend/env.example | 43 +++++++++ app/databuk/dashboard/backend/main.py | 13 --- app/databuk/dashboard/backend/pyproject.toml | 51 ++++++++++ .../dashboard/backend/requirements.txt | 10 -- env.example | 40 +------- 9 files changed, 188 insertions(+), 134 deletions(-) create mode 100644 app/databuk/dashboard/backend/.gitignore create mode 100644 app/databuk/dashboard/backend/env.example create mode 100644 app/databuk/dashboard/backend/pyproject.toml delete mode 100644 app/databuk/dashboard/backend/requirements.txt diff --git a/app/databuk/dashboard/README.md b/app/databuk/dashboard/README.md index 32f4265e..a47b7106 100644 --- a/app/databuk/dashboard/README.md +++ b/app/databuk/dashboard/README.md @@ -31,13 +31,17 @@ backend/ ├── services/ │ └── s3_service.py # Core S3 and Zarr operations ├── routers/ -│ ├── s3.py # S3 API endpoints -│ └── config.py # Configuration management +│ ├── s3.py # S3 API endpoints +│ ├── logs.py # Logs endpoint +│ └── config.py # Configuration management ├── core/ -│ └── config_manager.py # YAML configuration handling +│ ├── config_manager.py # YAML configuration handling +│ └── config.py # Settings (loads backend/.env) ├── config/ -│ └── endpoints.yaml # S3 endpoint configuration -└── main.py # FastAPI application +│ └── endpoints.yaml # S3 endpoint configuration +├── pyproject.toml # Backend packaging and deps +├── env.example # Example env (copy to .env) +└── main.py # FastAPI application ``` ## Quick Start @@ -53,40 +57,27 @@ backend/ git clone cd zarr_fuse/app/databuk/dashboard -# Install zarr_fuse first (from project root) -cd ../../../ -pip install -e . - -# Install dashboard dependencies -cd app/databuk/dashboard npm install ``` ### 2. Backend Setup -```bash +```powershell cd backend - -# Install web framework dependencies only -# (zarr_fuse provides S3/Zarr functionality) -pip install fastapi uvicorn pydantic - -# Configure S3 credentials in root .env -# (See Configuration section) +python -m venv venv +.\venv\Scripts\Activate.ps1 +pip install -e .[dev] ``` ### 3. Configuration -Create `.env` file in project root (copy from `env.example`): -```bash -# S3 Configuration -# Contact project maintainers for access credentials +Create `.env` file in `backend/` (copy from `backend/env.example`): +```ini +# S3 Configuration (placeholders) S3_BUCKET_NAME=your_bucket_name S3_ACCESS_KEY=your_access_key_here S3_SECRET_KEY=your_secret_key_here -S3_ENDPOINT_URL=https://s3.cl4.du.cesnet.cz +S3_ENDPOINT_URL=https://s3.example.com S3_ADDRESSING_STYLE=path - -# Multiple bucket configurations available in env.example ``` Configure `backend/config/endpoints.yaml`: @@ -107,12 +98,12 @@ Configure `backend/config/endpoints.yaml`: ``` ### 4. Run Application -```bash +```powershell # Terminal 1: Start backend cd backend -python main.py +python run.py -# Terminal 2: Start frontend +# Terminal 2: Start frontend cd .. # back to dashboard root npm run dev ``` @@ -169,7 +160,6 @@ Works with any Zarr group/array hierarchy! ## API Endpoints ### Configuration -- `GET /api/config/current` - Get current endpoint config - `GET /api/config/endpoints` - Get all endpoints ### S3 Operations @@ -240,9 +230,9 @@ Works with any Zarr group/array hierarchy! ## File Locations ### Configuration -- **S3 Credentials**: `/.env` (project root) +- **S3 Credentials**: `backend/.env` - **Endpoint Config**: `backend/config/endpoints.yaml` -- **Dependencies**: `backend/requirements.txt`, `package.json` +- **Dependencies**: `backend/pyproject.toml`, `package.json` ### Core Files - **Main App**: `src/App.tsx` diff --git a/app/databuk/dashboard/backend/.gitignore b/app/databuk/dashboard/backend/.gitignore new file mode 100644 index 00000000..da9416d1 --- /dev/null +++ b/app/databuk/dashboard/backend/.gitignore @@ -0,0 +1,6 @@ +venv/ +.env +dist/ +build/ +*.egg-info/ +__pycache__/ diff --git a/app/databuk/dashboard/backend/README.md b/app/databuk/dashboard/backend/README.md index 6fd847d5..c8afecbd 100644 --- a/app/databuk/dashboard/backend/README.md +++ b/app/databuk/dashboard/backend/README.md @@ -1,80 +1,96 @@ # ZARR FUSE Dashboard Backend -Modular FastAPI backend for exploring Zarr stores and building tree structures. +Modular FastAPI backend for exploring Zarr stores on S3. Now packaged as a standalone Python project with its own `pyproject.toml` and `.env` inside `backend/`. ## Architecture ``` backend/ -├── core/ # Configuration and utilities -├── models/ # Pydantic response models -├── services/ # Business logic (Zarr operations) -├── routers/ # HTTP API endpoints -├── main.py # FastAPI application -├── run.py # Server startup script -└── requirements.txt # Python dependencies +├── core/ # Configuration and utilities +├── services/ # Business logic (Zarr operations) +├── routers/ # HTTP API endpoints (s3, logs, config) +├── config/ # YAML endpoint configs (endpoints.yaml) +├── main.py # FastAPI application +├── run.py # Server startup script +├── pyproject.toml # Packaging and dependencies +└── env.example # Example env vars (copy to .env) ``` ## Setup -1. **Install dependencies:** - ```bash - cd backend - pip install -r requirements.txt +1. Create and activate a virtual environment (Windows PowerShell): + ```powershell + cd app/databuk/dashboard/backend + python -m venv venv + .\venv\Scripts\Activate.ps1 ``` -2. **Run the server:** - ```bash +2. Install package (editable) with dev extras: + ```powershell + pip install -e .[dev] + ``` + +3. Configure environment variables: + - Copy `env.example` to `.env` + - Fill `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_ENDPOINT_URL`, `S3_BUCKET_NAME`, etc. + +4. Run the server: + ```powershell python run.py ``` - Or directly with uvicorn: - ```bash + ```powershell uvicorn main:app --reload --host 0.0.0.0 --port 8000 ``` ## API Endpoints -### Tree Structure -- `GET /api/tree/structure` - Get complete tree hierarchy -- `GET /api/tree/node` - Get specific node information -- `GET /api/tree/store/summary` - Get store summary +### Configuration +- `GET /api/config/endpoints` - List configured endpoints + +### S3 Operations +- `GET /api/s3/status` - Connection status +- `GET /api/s3/structure` - Current store structure +- `GET /api/s3/node/{store_name}/{node_path}` - Node details +- `GET /api/s3/variable/{store_name}/{variable_path}` - Variable data + +### Logs +- `GET /api/logs` - Backend warnings and errors from latest log ### Health & Info - `GET /` - API information - `GET /health` - Health check -- `GET /docs` - Interactive API documentation (Swagger UI) +- `GET /docs` - Swagger UI ## Configuration -The backend automatically detects test stores from the project structure: -- **structure_tree.zarr**: Located at `zarr_fuse/test/workdir/structure_tree.zarr` -- **CORS**: Configured for frontend development (`localhost:5173`) +Environment variables are loaded only from `backend/.env`. + +- `.env`: S3 credentials and settings (see `env.example`) +- `config/endpoints.yaml`: defines STORE_URL and S3 options per endpoint +- CORS: enabled for `http://localhost:5173` ## Data Flow -1. **S3Service**: Opens and explores Zarr stores from S3 -2. **S3Router**: Exposes HTTP endpoints for S3 operations -3. **Frontend**: Consumes data for sidebar rendering and variable display +1. S3Service: Opens and explores Zarr stores from S3 +2. S3Router: Exposes HTTP endpoints for S3 operations +3. LogsRouter: Exposes backend logs to the UI +4. Frontend: Consumes data for tree and variable views ## Testing -Test the API: -```bash -# Health check +```powershell +# Health curl http://localhost:8000/health -# Get tree structure -curl http://localhost:8000/api/tree/structure +# Structure +curl http://localhost:8000/api/s3/structure -# Get specific node -curl "http://localhost:8000/api/tree/node?path=root" +# Logs +curl http://localhost:8000/api/logs ``` -## Next Steps +## Notes -- [ ] Add weather data endpoints -- [ ] Implement data sampling for plotting -- [ ] Add authentication/authorization -- [ ] Implement caching for large trees -- [ ] Add metrics and monitoring +- NaN/Infinity are serialized as strings for JSON compliance +- Uses Zarr FsspecStore and list_dir for S3-backed stores diff --git a/app/databuk/dashboard/backend/core/config.py b/app/databuk/dashboard/backend/core/config.py index d2c773c3..ec99d313 100644 --- a/app/databuk/dashboard/backend/core/config.py +++ b/app/databuk/dashboard/backend/core/config.py @@ -1,6 +1,15 @@ import os from pathlib import Path from typing import Optional +from dotenv import load_dotenv + +CURRENT_DIR = Path(__file__).resolve().parent.parent # points to backend/ + +# Strictly load only backend-local .env (no fallback to repo root) +BACKEND_ENV_PATH = CURRENT_DIR / ".env" +if BACKEND_ENV_PATH.exists(): + load_dotenv(BACKEND_ENV_PATH) + class Settings: """Backend configuration settings.""" diff --git a/app/databuk/dashboard/backend/env.example b/app/databuk/dashboard/backend/env.example new file mode 100644 index 00000000..7b0fd14b --- /dev/null +++ b/app/databuk/dashboard/backend/env.example @@ -0,0 +1,43 @@ +# S3 Configuration +# Copy this file to .env and fill in your actual credentials +# Contact project maintainers for access credentials + +S3_BUCKET_NAME=hlavo-release +S3_ACCESS_KEY=your_access_key_here +S3_SECRET_KEY=your_secret_key_here +S3_ENDPOINT_URL=https://s3.cl4.du.cesnet.cz +S3_ADDRESSING_STYLE=path + +#-------------------------------------------------------------------------------------------------------------------------------------------# +# Alternative Bucket: test-zarr-storage +# S3_BUCKET_NAME=test-zarr-storage +# S3_ACCESS_KEY=your_test_access_key_here +# S3_SECRET_KEY=your_test_secret_key_here + +# Store URLs to test +# Test tree structure (child_1, child_2, child_3) +#STORE_URL: "s3://${S3_BUCKET_NAME}/dashboard-test/structure_tree.zarr" + +# Python 3.11 tree +#STORE_URL: "s3://${S3_BUCKET_NAME}/py311/structure_tree.zarr" + +# Python 3.12 tree +#STORE_URL: "s3://${S3_BUCKET_NAME}/py312/structure_tree.zarr" + +# Python 3.13 tree +#STORE_URL: "s3://${S3_BUCKET_NAME}/py313/structure_tree.zarr" + +# Python 3.11 weather (lat/lon/temp data) +#STORE_URL: "s3://${S3_BUCKET_NAME}/py311/structure_weather.zarr" + +# Python 3.12 weather (currently tested) +#STORE_URL: "s3://${S3_BUCKET_NAME}/py312/structure_weather.zarr" + +# Python 3.13 weather +#STORE_URL: "s3://${S3_BUCKET_NAME}/py313/structure_weather.zarr" + +#-------------------------------------------------------------------------------------------------------------------------------------------# +# hlavo-release bucket store URLs +#STORE_URL: "s3://${S3_BUCKET_NAME}/surface.zarr" +#STORE_URL: "s3://${S3_BUCKET_NAME}/dashboard-test/structure_tree.zarr" +#STORE_URL: "s3://${S3_BUCKET_NAME}/hlavo-release" \ No newline at end of file diff --git a/app/databuk/dashboard/backend/main.py b/app/databuk/dashboard/backend/main.py index c6660a35..175f9c6d 100644 --- a/app/databuk/dashboard/backend/main.py +++ b/app/databuk/dashboard/backend/main.py @@ -2,19 +2,6 @@ from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager from pathlib import Path -import os -from dotenv import load_dotenv - -# Load environment variables from .env file in root directory -env_path = Path("C:/Users/fatih/Documents/GitHub/zarr_fuse/.env") -load_dotenv(env_path) - -# Debug: Check if environment variables are loaded -print(f"Environment variables loaded:") -print(f" S3_ACCESS_KEY: {'Found' if os.getenv('S3_ACCESS_KEY') else 'Not found'}") -print(f" S3_SECRET_KEY: {'Found' if os.getenv('S3_SECRET_KEY') else 'Not found'}") -print(f" S3_BUCKET_NAME: {'Found' if os.getenv('S3_BUCKET_NAME') else 'Not found'}") -print(f" .env path: {env_path}") # Use absolute imports from core.config import settings diff --git a/app/databuk/dashboard/backend/pyproject.toml b/app/databuk/dashboard/backend/pyproject.toml new file mode 100644 index 00000000..8bf008a9 --- /dev/null +++ b/app/databuk/dashboard/backend/pyproject.toml @@ -0,0 +1,51 @@ +[build-system] +requires = ["setuptools>=61", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "databuk-dashboard-backend" +version = "0.1.0" +description = "Databuk Dashboard backend (FastAPI) for Zarr store exploration" +readme = "README.md" +requires-python = ">=3.11" +authors = [{ name = "Databuk", email = "dev@example.com" }] + +# S3/Zarr ekosistemi + backend web bağımlılıkları +dependencies = [ + "aiobotocore>=2.18.0", + "PyYAML", + "attrs", + "tzdata", + "numpy", + "scipy", + "polars", + "pint", + "pint-xarray", + "anytree", + "pandas", + "pyarrow", + "seaborn", + "s3fs", + "fsspec", + "zarr", + "dask", + "xarray", + "python-dotenv", + # Backend web: + "fastapi", + "uvicorn[standard]", + "pydantic", +] + +[project.optional-dependencies] +dev = [ + "pytest", + "httpx", + "ruff", + "mypy", +] + +[tool.setuptools.packages.find] +where = ["."] +include = ["core*", "routers*", "services*", "config*", "models*", "*"] +exclude = ["venv*", "node_modules*", "dist*", "build*"] diff --git a/app/databuk/dashboard/backend/requirements.txt b/app/databuk/dashboard/backend/requirements.txt deleted file mode 100644 index 7f2632b7..00000000 --- a/app/databuk/dashboard/backend/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -fastapi==0.104.1 -uvicorn[standard]==0.24.0 -pydantic==2.5.0 -# zarr==2.16.1 # Already in zarr_fuse -# numpy>=1.26.0 # Already in zarr_fuse -# PyYAML==6.0.1 # Already in zarr_fuse -# s3fs>=2024.6.0,<2025.0 # Already in zarr_fuse -# fsspec>=2024.6.0,<2025.0 # Already in zarr_fuse -# aiobotocore>=2.18.0 # Already in zarr_fuse -# boto3 # Already in zarr_fuse diff --git a/env.example b/env.example index 7b0fd14b..d6018b4a 100644 --- a/env.example +++ b/env.example @@ -1,43 +1,5 @@ -# S3 Configuration -# Copy this file to .env and fill in your actual credentials -# Contact project maintainers for access credentials - S3_BUCKET_NAME=hlavo-release S3_ACCESS_KEY=your_access_key_here S3_SECRET_KEY=your_secret_key_here S3_ENDPOINT_URL=https://s3.cl4.du.cesnet.cz -S3_ADDRESSING_STYLE=path - -#-------------------------------------------------------------------------------------------------------------------------------------------# -# Alternative Bucket: test-zarr-storage -# S3_BUCKET_NAME=test-zarr-storage -# S3_ACCESS_KEY=your_test_access_key_here -# S3_SECRET_KEY=your_test_secret_key_here - -# Store URLs to test -# Test tree structure (child_1, child_2, child_3) -#STORE_URL: "s3://${S3_BUCKET_NAME}/dashboard-test/structure_tree.zarr" - -# Python 3.11 tree -#STORE_URL: "s3://${S3_BUCKET_NAME}/py311/structure_tree.zarr" - -# Python 3.12 tree -#STORE_URL: "s3://${S3_BUCKET_NAME}/py312/structure_tree.zarr" - -# Python 3.13 tree -#STORE_URL: "s3://${S3_BUCKET_NAME}/py313/structure_tree.zarr" - -# Python 3.11 weather (lat/lon/temp data) -#STORE_URL: "s3://${S3_BUCKET_NAME}/py311/structure_weather.zarr" - -# Python 3.12 weather (currently tested) -#STORE_URL: "s3://${S3_BUCKET_NAME}/py312/structure_weather.zarr" - -# Python 3.13 weather -#STORE_URL: "s3://${S3_BUCKET_NAME}/py313/structure_weather.zarr" - -#-------------------------------------------------------------------------------------------------------------------------------------------# -# hlavo-release bucket store URLs -#STORE_URL: "s3://${S3_BUCKET_NAME}/surface.zarr" -#STORE_URL: "s3://${S3_BUCKET_NAME}/dashboard-test/structure_tree.zarr" -#STORE_URL: "s3://${S3_BUCKET_NAME}/hlavo-release" \ No newline at end of file +S3_ADDRESSING_STYLE=path \ No newline at end of file From e574018dc12fa998051d6945e70a8b5300c0e908 Mon Sep 17 00:00:00 2001 From: "stepan.moc" Date: Mon, 6 Oct 2025 17:01:57 +0200 Subject: [PATCH 10/12] =?UTF-8?q?feat(ci,helm,oci):=20prepared=20container?= =?UTF-8?q?files,=20helm=20chart=20and=20CI=20for=20das=E2=80=A6=20(#39)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(ci,helm,oci): prepared containerfiles, helm chart and CI for dashboard * fix(ci): workflow trigger * fix(ci): removed error from custom actions * fix(ci): add registry username and password for containerize action * fix(ci): removed error msg * feat(ci): small improvements * fix(ci): changed artifact path * fix(ci): chanched concurrency * feat(ci): improved concurrency and testing deploy * fea(ci,helm): changed conteinerize for ingress server and add seccompProfile to security context for dashboard chart * fix(helm): changed configmap name * feat(ci,helm): small improvements * fix(helm): configmap * feaet(react,helm): changed base url for api calls * feat(dashboard): add env development * fix(dashboard): self review * fix(helm): removed rewrite target annotaion * fix(ci): change vite api url * fix(ci): removed deploy for dashboard pull request --- .github/actions/containerize/action.yaml | 52 ++++++ .github/actions/helm-lint/action.yaml | 25 +++ .github/workflows/dashboard-pull-request.yaml | 33 ++++ .github/workflows/dashboard-push-main.yaml | 33 ++++ .../dashboard-reusable-workflow.yaml | 176 ++++++++++++++++++ .../ingress-server-pull-request.yaml | 8 +- .../workflows/ingress-server-push-main.yaml | 7 +- .../workflows/ingress-server-push-tag.yaml | 6 +- .../ingress-server-reusable-workflow.yaml | 55 +++--- app/databuk/dashboard/.env.development | 1 + .../dashboard/backend/config/endpoints.yaml | 15 +- .../dashboard/charts/dashboard/.helmignore | 23 +++ .../dashboard/charts/dashboard/Chart.yaml | 12 ++ .../templates/backend/configmap.yaml | 29 +++ .../templates/backend/deployment.yaml | 68 +++++++ .../dashboard/templates/backend/secret.yaml | 10 + .../dashboard/templates/backend/service.yaml | 14 ++ .../templates/frontend/deployment.yaml | 62 ++++++ .../dashboard/templates/frontend/service.yaml | 14 ++ .../charts/dashboard/templates/ingress.yaml | 34 ++++ .../dashboard/charts/dashboard/values.yaml | 106 +++++++++++ .../dashboard/oci/Containerfile.backend | 36 ++++ .../dashboard/oci/Containerfile.frontend | 29 +++ app/databuk/dashboard/oci/nginx.conf | 41 ++++ app/databuk/dashboard/src/App.tsx | 83 +++++---- app/databuk/dashboard/src/api.ts | 1 + .../dashboard/src/components/LogPanel.tsx | 30 ++- .../src/components/sidebar/Sidebar.tsx | 31 ++- app/databuk/dashboard/vite.config.ts | 2 +- .../ingress-server/templates/deployment.yaml | 16 +- .../ingress-server/templates/secret.yaml | 14 ++ .../src/inputs/endpoints_config.yaml | 6 + 32 files changed, 933 insertions(+), 139 deletions(-) create mode 100644 .github/actions/containerize/action.yaml create mode 100644 .github/actions/helm-lint/action.yaml create mode 100644 .github/workflows/dashboard-pull-request.yaml create mode 100644 .github/workflows/dashboard-push-main.yaml create mode 100644 .github/workflows/dashboard-reusable-workflow.yaml create mode 100644 app/databuk/dashboard/.env.development create mode 100644 app/databuk/dashboard/charts/dashboard/.helmignore create mode 100644 app/databuk/dashboard/charts/dashboard/Chart.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/templates/backend/configmap.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/templates/backend/deployment.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/templates/backend/secret.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/templates/backend/service.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/templates/frontend/deployment.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/templates/frontend/service.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/templates/ingress.yaml create mode 100644 app/databuk/dashboard/charts/dashboard/values.yaml create mode 100644 app/databuk/dashboard/oci/Containerfile.backend create mode 100644 app/databuk/dashboard/oci/Containerfile.frontend create mode 100644 app/databuk/dashboard/oci/nginx.conf create mode 100644 app/databuk/dashboard/src/api.ts create mode 100644 app/databuk/ingress_server/charts/ingress-server/templates/secret.yaml diff --git a/.github/actions/containerize/action.yaml b/.github/actions/containerize/action.yaml new file mode 100644 index 00000000..483e72d9 --- /dev/null +++ b/.github/actions/containerize/action.yaml @@ -0,0 +1,52 @@ +name: Containerize application +description: Build and optionally push a container image +inputs: + registry: + description: Container registry (e.g., docker.io, ghcr.io, etc.) + required: false + default: docker.io + repository: + description: Container repository (e.g., username/repo) + required: true + tag: + description: Tag for the container image + required: true + containerfile-path: + description: Path to the Dockerfile + required: false + default: oci/Containerfile + push: + description: Whether to push the container image + required: false + default: "false" + extra-build-args: + description: Build arguments for the container build (key=value pairs, comma-separated) + required: false + default: "" + registry-username: + description: Username for the container registry (if pushing) + required: true + registry-password: + description: Password for the container registry (if pushing) + required: true + working-directory: + description: The working directory to run the action in + required: true + +runs: + using: composite + steps: + - name: Login to registry + shell: bash + if: ${{ inputs.push }} == 'true' + run: buildah login -u ${{ inputs.registry-username }} -p "${{ inputs.registry-password }}" ${{ inputs.registry }} + - name: Build container file + shell: bash + working-directory: ${{ inputs.working-directory }} + run: buildah build -t ${{ inputs.registry }}/${{ inputs.repository }}:${{ inputs.tag }} ${{ inputs.extra-build-args }} -f ${{ inputs.containerfile-path }} . + - name: Push container image + shell: bash + if: ${{ inputs.push }} == 'true' + run: | + buildah push ${{ inputs.registry }}/${{ inputs.repository }}:${{ inputs.tag }} docker://${{ inputs.registry }}/${{ inputs.repository }}:${{ inputs.tag }} + buildah logout ${{ inputs.registry }} diff --git a/.github/actions/helm-lint/action.yaml b/.github/actions/helm-lint/action.yaml new file mode 100644 index 00000000..6dafcf5f --- /dev/null +++ b/.github/actions/helm-lint/action.yaml @@ -0,0 +1,25 @@ +name: Helm Lint +description: Lint a Helm chart +inputs: + working-directory: + description: The working directory to run the action in + required: true + chart: + description: Path to the Helm chart to lint + required: false + default: . + values: + description: Additional values to pass to helm lint command + required: false + default: "" +runs: + using: "composite" + steps: + - name: Lint helm chart + shell: bash + working-directory: ${{ inputs.working-directory }} + run: helm lint ${{ inputs.chart }} --strict ${{ inputs.values }} > /dev/null + - name: Template helm chart + shell: bash + working-directory: ${{ inputs.working-directory }} + run: helm template chart ${{ inputs.chart }} ${{ inputs.values }} > /dev/null diff --git a/.github/workflows/dashboard-pull-request.yaml b/.github/workflows/dashboard-pull-request.yaml new file mode 100644 index 00000000..bb22ef4a --- /dev/null +++ b/.github/workflows/dashboard-pull-request.yaml @@ -0,0 +1,33 @@ +name: Dashboard - pull request +on: + pull_request: + branches: + - "**" +jobs: + get-changed-files: + name: Get changed file + runs-on: ubuntu-latest + outputs: + any-changed: ${{ steps.changed.outputs.any_changed }} + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-get-changed-files + cancel-in-progress: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Check if Dashboard files were changed + id: changed + uses: tj-actions/changed-files@v45 + with: + files: | + app/databuk/dashboard/** + build: + name: Build and lint dashboard + needs: + - get-changed-files + if: ${{ needs.get-changed-files.outputs.any-changed }} == 'true' + uses: ./.github/workflows/dashboard-reusable-workflow.yaml + with: + tag: generate + deploy: false + secrets: inherit diff --git a/.github/workflows/dashboard-push-main.yaml b/.github/workflows/dashboard-push-main.yaml new file mode 100644 index 00000000..edb88c69 --- /dev/null +++ b/.github/workflows/dashboard-push-main.yaml @@ -0,0 +1,33 @@ +name: Dashboard - push main +on: + push: + branches: + - main + workflow_dispatch: +jobs: + get-changed-files: + name: Get changed file + runs-on: ubuntu-latest + outputs: + any-changed: ${{ steps.changed.outputs.any_changed }} + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-get-changed-files + cancel-in-progress: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Check if Dashboard files were changed + id: changed + uses: tj-actions/changed-files@v45 + with: + files: | + app/databuk/dashboard/** + build-and-deploy: + uses: ./.github/workflows/dashboard-reusable-workflow.yaml + needs: + - get-changed-files + if: always() && ( github.event_name == 'push' || needs.get-changed-files.outputs.any-changed == 'true' ) + with: + tag: generate + deploy: true + secrets: inherit diff --git a/.github/workflows/dashboard-reusable-workflow.yaml b/.github/workflows/dashboard-reusable-workflow.yaml new file mode 100644 index 00000000..51fea1a4 --- /dev/null +++ b/.github/workflows/dashboard-reusable-workflow.yaml @@ -0,0 +1,176 @@ +name: Dashboard - reusable workflow +on: + workflow_call: + inputs: + tag: + description: The tag to use for the container image + required: false + type: string + default: generate + deploy: + description: Whether to deploy the Helm chart + required: false + type: boolean + default: false +jobs: + get-version-tag: + name: Get version tag + runs-on: ubuntu-latest + outputs: + version-tag: ${{ steps.get_version_tag.outputs.version-tag }} + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-get-version-tag + cancel-in-progress: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Get version tag + id: get_version_tag + run: | + if [[ ${{ inputs.tag }} == "generate" ]]; then + echo "version-tag=ci-$(git rev-parse --short=7 HEAD)" >> $GITHUB_OUTPUT + else + echo "version-tag=${{ inputs.tag }}" >> $GITHUB_OUTPUT + fi + containerize-backend: + name: Containerize backend + runs-on: ubuntu-latest + container: + image: quay.io/buildah/stable:v1.35.4 + options: --privileged + needs: + - get-version-tag + defaults: + run: + working-directory: app/databuk/dashboard + concurrency: + group: "${{ github.workflow }}-${{ github.ref }}-containerize-backend" + cancel-in-progress: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Containerize + uses: ./.github/actions/containerize + with: + repository: jbrezmorf/zarr-fuse-dashboard-backend + tag: ${{ needs.get-version-tag.outputs.version-tag }} + containerfile-path: oci/Containerfile.backend + push: true + registry-username: ${{ vars.DOCKER_USERNAME }} + registry-password: ${{ secrets.DOCKER_PASSWORD }} + working-directory: app/databuk/dashboard + build-frontend: + name: Build frontend + runs-on: ubuntu-latest + container: + image: docker.io/node:24.8.0-trixie-slim + needs: + - get-version-tag + defaults: + run: + working-directory: app/databuk/dashboard + concurrency: + group: "${{ github.workflow }}-${{ github.ref }}-build-frontend" + cancel-in-progress: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install dependencies + run: npm ci + - name: Build frontend + env: + VITE_API_URL: "https://zarr-fuse-dashboard.dyn.cloud.e-infra.cz" + run: npm run build + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: dashboard-frontend + path: app/databuk/dashboard/dist + if-no-files-found: error + containerize-frontend: + name: Containerize frontend + runs-on: ubuntu-latest + container: + image: quay.io/buildah/stable:v1.35.4 + options: --privileged + needs: + - get-version-tag + - build-frontend + defaults: + run: + working-directory: app/databuk/dashboard + concurrency: + group: "${{ github.workflow }}-${{ github.ref }}-containerize-frontend" + cancel-in-progress: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: dashboard-frontend + path: app/databuk/dashboard/dist + - name: Containerize + uses: ./.github/actions/containerize + with: + repository: jbrezmorf/zarr-fuse-dashboard-frontend + tag: ${{ needs.get-version-tag.outputs.version-tag }} + containerfile-path: oci/Containerfile.frontend + push: true + registry-username: ${{ vars.DOCKER_USERNAME }} + registry-password: ${{ secrets.DOCKER_PASSWORD }} + working-directory: app/databuk/dashboard + lint-helm-chart: + name: Lint dashboard Helm chart + runs-on: ubuntu-latest + container: + image: docker.io/alpine/k8s:1.29.14 + defaults: + run: + working-directory: app/databuk/dashboard/charts/dashboard + concurrency: + group: "${{ github.workflow }}-${{ github.ref }}-lint-helm-chart" + cancel-in-progress: true + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Lint helm chart + uses: ./.github/actions/helm-lint + with: + working-directory: app/databuk/dashboard/charts/dashboard + deploy: + name: Deploy dashboard to e-infra rancher + runs-on: ubuntu-latest + if: ${{ inputs.deploy }} + container: + image: docker.io/alpine/k8s:1.29.14 + needs: + - lint-helm-chart + - get-version-tag + - containerize-backend + - containerize-frontend + env: + KUBECONFIG: ./kubeconfig.yaml + defaults: + run: + working-directory: app/databuk/dashboard/charts + concurrency: + group: "${{ github.workflow }}-${{ github.ref }}-deploy" + cancel-in-progress: false + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Decode kubeconfig + run: echo "${{ secrets.KUBECONFIG }}" | base64 -d > kubeconfig.yaml && chmod 600 kubeconfig.yaml + - name: Ensure that the namespace exists + run: kubectl create namespace --dry-run=client -o yaml zarr-fuse-dashboard + - name: Set context + run: kubectl config set-context --current --namespace=zarr-fuse-dashboard + - name: Deploy app + run: | + helm upgrade dashboard dashboard \ + --install --atomic --timeout 10m --namespace zarr-fuse-dashboard \ + --set backend.image.tag="${{ needs.get-version-tag.outputs.version-tag }}" \ + --set frontend.image.tag="${{ needs.get-version-tag.outputs.version-tag }}" \ + --set backend.secrets.s3.accessKey="${{ secrets.S3_ACCESS_KEY }}" \ + --set backend.secrets.s3.secretKey="${{ secrets.S3_SECRET_KEY }}" diff --git a/.github/workflows/ingress-server-pull-request.yaml b/.github/workflows/ingress-server-pull-request.yaml index 00ef6908..63e529ba 100644 --- a/.github/workflows/ingress-server-pull-request.yaml +++ b/.github/workflows/ingress-server-pull-request.yaml @@ -2,7 +2,7 @@ name: Ingress server - pull request on: pull_request: branches: - - "*" + - "**" jobs: get-changed-files: name: Get changed file @@ -10,7 +10,7 @@ jobs: outputs: any-changed: ${{ steps.changed.outputs.any_changed }} concurrency: - group: ${{ github.head_ref }}-${{ github.ref_name }}-get-changed-files + group: ${{ github.workflow }}-${{ github.ref }}-get-changed-files cancel-in-progress: true steps: - name: Checkout @@ -20,7 +20,6 @@ jobs: uses: tj-actions/changed-files@v45 with: files: | - charts/ingress-server/** app/databuk/ingress_server/** build: name: Build and deploy ingress server @@ -29,8 +28,7 @@ jobs: if: ${{ needs.get-changed-files.outputs.any-changed }} == 'true' uses: ./.github/workflows/ingress-server-reusable-workflow.yaml with: - push-container: true - deploy-helm-chart: true + deploy: true tag: generate namespace: ingress-server-development release-name: ingress-server-development diff --git a/.github/workflows/ingress-server-push-main.yaml b/.github/workflows/ingress-server-push-main.yaml index e7377a3f..d0650310 100644 --- a/.github/workflows/ingress-server-push-main.yaml +++ b/.github/workflows/ingress-server-push-main.yaml @@ -11,7 +11,7 @@ jobs: outputs: any-changed: ${{ steps.changed.outputs.any_changed }} concurrency: - group: ${{ github.head_ref }}-${{ github.ref_name }}-get-changed-files + group: ${{ github.workflow }}-${{ github.ref }}-get-changed-files cancel-in-progress: true steps: - name: Checkout @@ -26,10 +26,9 @@ jobs: uses: ./.github/workflows/ingress-server-reusable-workflow.yaml needs: - get-changed-files - if: always() && github.event_name == 'push' || needs.get-changed-files.outputs.any-changed == 'true' + if: always() && ( github.event_name == 'push' || needs.get-changed-files.outputs.any-changed == 'true' ) with: - push-container: true - deploy-helm-chart: true + deploy: true tag: generate namespace: ingress-server-latest release-name: ingress-server-latest diff --git a/.github/workflows/ingress-server-push-tag.yaml b/.github/workflows/ingress-server-push-tag.yaml index a9e1fd2e..ea8f7109 100644 --- a/.github/workflows/ingress-server-push-tag.yaml +++ b/.github/workflows/ingress-server-push-tag.yaml @@ -9,6 +9,9 @@ jobs: runs-on: ubuntu-latest outputs: version: ${{ steps.get-version.outputs.version }} + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-get-changed-files + cancel-in-progress: true steps: - name: Checkout uses: actions/checkout@v4 @@ -23,8 +26,7 @@ jobs: - get-version if: needs.get-version.outputs.version != '' with: - push-container: true - deploy-helm-chart: true + deploy: true tag: ${{ needs.get-version.outputs.version }} namespace: ingress-server release-name: ingress-server diff --git a/.github/workflows/ingress-server-reusable-workflow.yaml b/.github/workflows/ingress-server-reusable-workflow.yaml index 6690bbf0..ca3ee2f8 100644 --- a/.github/workflows/ingress-server-reusable-workflow.yaml +++ b/.github/workflows/ingress-server-reusable-workflow.yaml @@ -2,12 +2,7 @@ name: Ingress server - reusable workflow on: workflow_call: inputs: - push-container: - description: Whether to push the container image - required: false - type: boolean - default: false - deploy-helm-chart: + deploy: description: Whether to deploy the Helm chart required: false type: boolean @@ -29,9 +24,6 @@ on: description: The S3 store URL (e.g., s3://bucket-name/path/to/store.zarr) required: true type: string -env: - repository: jbrezmorf/zarr-fuse-ingress-server - registry: docker.io jobs: get-version-tag: name: Get version tag @@ -39,7 +31,7 @@ jobs: outputs: version-tag: ${{ steps.get_version_tag.outputs.version-tag }} concurrency: - group: ${{ github.head_ref }}-${{ github.ref_name }}-get-version-tag + group: ${{ github.workflow }}-${{ github.ref }}-get-version-tag cancel-in-progress: true steps: - name: Checkout @@ -69,24 +61,22 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to registry - if: ${{ inputs.push-container }} - run: buildah login -u ${{ vars.DOCKER_USERNAME }} -p "${{ secrets.DOCKER_PASSWORD }}" ${{ env.registry }} - - name: Build container file - run: | - buildah build -t ${{ env.registry }}/${{ env.repository }}:${{ needs.get-version-tag.outputs.version-tag }} \ - --build-arg APP_VERSION="${{ needs.get-version-tag.outputs.version-tag }}" \ - -f oci/Containerfile . - - name: Push container image - if: ${{ inputs.push-container }} - run: | - buildah push ${{ env.registry }}/${{ env.repository }}:${{ needs.get-version-tag.outputs.version-tag }} docker://${{ env.registry }}/${{ env.repository }}:${{ needs.get-version-tag.outputs.version-tag }} - buildah logout ${{ env.registry }} + - name: Containerize + uses: ./.github/actions/containerize + with: + repository: jbrezmorf/zarr-fuse-ingress-server + tag: ${{ needs.get-version-tag.outputs.version-tag }} + containerfile-path: oci/Containerfile + push: true + registry-username: ${{ vars.DOCKER_USERNAME }} + registry-password: ${{ secrets.DOCKER_PASSWORD }} + working-directory: app/databuk/ingress_server + extra-build-args: --build-arg APP_VERSION="${{ needs.get-version-tag.outputs.version-tag }}" lint-helm-chart: name: Lint ingress server Helm chart runs-on: ubuntu-latest container: - image: alpine/k8s:1.29.14 + image: docker.io/alpine/k8s:1.29.14 needs: - containerize defaults: @@ -99,15 +89,16 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Lint helm chart - run: helm lint --values values/minimal-required-values.yaml --strict > /dev/null - - name: Template helm chart - run: helm template ingress-server . --values values/minimal-required-values.yaml > /dev/null - deploy-helm-chart: + uses: ./.github/actions/helm-lint + with: + working-directory: app/databuk/ingress_server/charts/ingress-server + values: --values values/minimal-required-values.yaml + deploy: name: Deploy ingress server to e-infra rancher runs-on: ubuntu-latest - if: ${{ inputs.deploy-helm-chart }} + if: ${{ inputs.deploy }} container: - image: alpine/k8s:1.29.14 + image: docker.io/alpine/k8s:1.29.14 needs: - lint-helm-chart - get-version-tag @@ -117,8 +108,8 @@ jobs: run: working-directory: app/databuk/ingress_server/charts concurrency: - group: "${{ github.workflow }}-${{ github.ref }}-deploy-helm-chart" - cancel-in-progress: true + group: "${{ github.workflow }}-${{ github.ref }}-deploy" + cancel-in-progress: false steps: - name: Checkout uses: actions/checkout@v4 diff --git a/app/databuk/dashboard/.env.development b/app/databuk/dashboard/.env.development new file mode 100644 index 00000000..b0e8fc8b --- /dev/null +++ b/app/databuk/dashboard/.env.development @@ -0,0 +1 @@ +VITE_API_URL=http://localhost:8000/api diff --git a/app/databuk/dashboard/backend/config/endpoints.yaml b/app/databuk/dashboard/backend/config/endpoints.yaml index bded14bc..050b6775 100644 --- a/app/databuk/dashboard/backend/config/endpoints.yaml +++ b/app/databuk/dashboard/backend/config/endpoints.yaml @@ -3,28 +3,27 @@ "test_s3_endpoint": # Reload settings - Reload_interval: 300 # 5 minutes in seconds - + Reload_interval: 300 # 5 minutes in seconds + # Schema configuration Schema_file: "schemas/test_schema.yaml" - - # S3 Store configuration - Using new credentials - STORE_URL: "s3://${S3_BUCKET_NAME}/dashboard-test/structure_tree.zarr" + + # S3 Store configuration - Using new credentials + STORE_URL: "s3://${S3_BUCKET_NAME}/bukov.zarr" S3_ENDPOINT_URL: "${S3_ENDPOINT_URL}" S3_access_key: "${S3_ACCESS_KEY}" S3_secret_key: "${S3_SECRET_KEY}" - + # Additional Zarr FUSE settings S3_region: "us-east-1" S3_use_ssl: true S3_verify_ssl: true - + # Store metadata Description: "Surface Zarr store with weather data (CESNET)" Store_type: "zarr" Version: "1.0.0" - # Example for multiple endpoints (commented for future use) # "production_s3_endpoint": # Reload_interval: 600 diff --git a/app/databuk/dashboard/charts/dashboard/.helmignore b/app/databuk/dashboard/charts/dashboard/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/app/databuk/dashboard/charts/dashboard/Chart.yaml b/app/databuk/dashboard/charts/dashboard/Chart.yaml new file mode 100644 index 00000000..066979f4 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v2 +name: dashboard +description: Zarr fuse dashboard to visualize and manage Zarr stores +type: application +version: 1.0.0 +appVersion: "1.0.0" +home: https://github.com/geomop/zarr_fuse +sources: + - https://github.com/geomop/zarr_fuse +maintainers: + - name: Stepan Moc + email: stepan.mocik@gmail.com diff --git a/app/databuk/dashboard/charts/dashboard/templates/backend/configmap.yaml b/app/databuk/dashboard/charts/dashboard/templates/backend/configmap.yaml new file mode 100644 index 00000000..05b6b957 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/templates/backend/configmap.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.backend.name }}-config + labels: + app.kubernetes.io/name: {{ .Values.backend.name }} +data: + endpoints.yaml: |- + "production_s3_endpoint": + # Reload settings + Reload_interval: 300 # 5 minutes in seconds + + # Schema configuration + Schema_file: "schemas/production_schema.yaml" + + # S3 Store configuration - Using new credentials + STORE_URL: "s3://app-databuk-release-service/bukov.zarr" + + S3_ENDPOINT_URL: "https://s3.cl4.du.cesnet.cz" + S3_access_key: "{{ .Values.backend.secrets.s3.accessKey }}" + S3_secret_key: "{{ .Values.backend.secrets.s3.secretKey }}" + + S3_region: "us-east-1" + S3_use_ssl: true + S3_verify_ssl: true + + Description: "Surface Zarr store with weather data (CESNET)" + Store_type: "zarr" + Version: "1.0.0" diff --git a/app/databuk/dashboard/charts/dashboard/templates/backend/deployment.yaml b/app/databuk/dashboard/charts/dashboard/templates/backend/deployment.yaml new file mode 100644 index 00000000..0239992b --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/templates/backend/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.backend.name }} + labels: + app.kubernetes.io/name: {{ .Values.backend.name }} +spec: + replicas: {{ .Values.backend.replicaCount | int }} + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: {{ .Values.backend.name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ .Values.backend.name }} + spec: + securityContext: + {{- toYaml .Values.backend.securityContext.pod | nindent 8 }} + volumes: + - name: dashboard-tmp + emptyDir: {} + - name: dashboard-config + configMap: + name: {{ .Values.backend.name }}-config + containers: + - name: {{ .Values.backend.name }} + image: "{{ .Values.registry.host }}/{{ .Values.backend.image.name }}:{{ .Values.backend.image.tag }}" + imagePullPolicy: {{ .Values.backend.image.pullPolicy }} + ports: + - name: {{ .Values.backend.service.internalPort.name | quote }} + containerPort: {{ .Values.backend.service.internalPort.number | int }} + envFrom: + - secretRef: + name: {{ .Values.backend.name }}-secrets + resources: + {{- toYaml .Values.backend.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.backend.securityContext.container | nindent 12 }} + volumeMounts: + - name: dashboard-tmp + mountPath: /tmp + - name: dashboard-config + mountPath: /app/config/endpoints.yaml + subPath: endpoints.yaml + readOnly: true + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + successThreshold: 1 + failureThreshold: 72 + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 10 + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 10 diff --git a/app/databuk/dashboard/charts/dashboard/templates/backend/secret.yaml b/app/databuk/dashboard/charts/dashboard/templates/backend/secret.yaml new file mode 100644 index 00000000..e930e088 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/templates/backend/secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.backend.name }}-secrets + labels: + app.kubernetes.io/name: {{ .Values.backend.name }} +type: Opaque +stringData: + S3_ACCESS_KEY: {{ .Values.backend.secrets.s3.accessKey | quote }} + S3_SECRET_KEY: {{ .Values.backend.secrets.s3.secretKey | quote }} diff --git a/app/databuk/dashboard/charts/dashboard/templates/backend/service.yaml b/app/databuk/dashboard/charts/dashboard/templates/backend/service.yaml new file mode 100644 index 00000000..1c38a242 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/templates/backend/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.backend.name }} + labels: + app.kubernetes.io/name: {{ .Values.backend.name }} +spec: + type: {{ .Values.backend.service.type }} + selector: + app.kubernetes.io/name: {{ .Values.backend.name }} + ports: + - name: {{ .Values.backend.service.externalPort.name | quote }} + port: {{ .Values.backend.service.externalPort.number | int }} + targetPort: {{ .Values.backend.service.internalPort.name | quote }} diff --git a/app/databuk/dashboard/charts/dashboard/templates/frontend/deployment.yaml b/app/databuk/dashboard/charts/dashboard/templates/frontend/deployment.yaml new file mode 100644 index 00000000..7d410508 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/templates/frontend/deployment.yaml @@ -0,0 +1,62 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.frontend.name }} + labels: + app.kubernetes.io/name: {{ .Values.frontend.name }} +spec: + replicas: {{ .Values.frontend.replicaCount | int }} + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: {{ .Values.frontend.name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ .Values.frontend.name }} + spec: + securityContext: + {{- toYaml .Values.frontend.securityContext.pod | nindent 8 }} + volumes: + - name: tmp + emptyDir: {} + - name: nginx-tmp + emptyDir: {} + containers: + - name: {{ .Values.frontend.name }} + image: "{{ .Values.registry.host }}/{{ .Values.frontend.image.name }}:{{ .Values.frontend.image.tag }}" + imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} + ports: + - name: {{ .Values.frontend.service.internalPort.name | quote }} + containerPort: {{ .Values.frontend.service.internalPort.number | int }} + resources: + {{- toYaml .Values.frontend.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.frontend.securityContext.container | nindent 12 }} + volumeMounts: + - name: tmp + mountPath: "/tmp" + - name: nginx-tmp + mountPath: "/var/lib/nginx/tmp" + startupProbe: + httpGet: + path: /health + port: http + periodSeconds: 5 + successThreshold: 1 + failureThreshold: 72 + livenessProbe: + httpGet: + path: /health + port: http + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 10 + readinessProbe: + httpGet: + path: /health + port: http + periodSeconds: 30 + successThreshold: 1 + failureThreshold: 10 diff --git a/app/databuk/dashboard/charts/dashboard/templates/frontend/service.yaml b/app/databuk/dashboard/charts/dashboard/templates/frontend/service.yaml new file mode 100644 index 00000000..8d2e4ad0 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/templates/frontend/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.frontend.name }} + labels: + app.kubernetes.io/name: {{ .Values.frontend.name }} +spec: + type: {{ .Values.frontend.service.type }} + selector: + app.kubernetes.io/name: {{ .Values.frontend.name }} + ports: + - name: {{ .Values.frontend.service.externalPort.name | quote }} + port: {{ .Values.frontend.service.externalPort.number | int }} + targetPort: {{ .Values.frontend.service.internalPort.name | quote }} diff --git a/app/databuk/dashboard/charts/dashboard/templates/ingress.yaml b/app/databuk/dashboard/charts/dashboard/templates/ingress.yaml new file mode 100644 index 00000000..e2e7cc88 --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/templates/ingress.yaml @@ -0,0 +1,34 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: dashboard-ingress + labels: + app.kubernetes.io/name: dashboard-ingress + {{- if .Values.ingress.annotations }} + annotations: + {{ .Values.ingress.annotations | toYaml | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className | quote }} + rules: + - host: zarr-fuse-dashboard.dyn.cloud.e-infra.cz + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Values.frontend.name }} + port: + number: {{ .Values.frontend.service.externalPort.number | int }} + - path: /api + pathType: Prefix + backend: + service: + name: {{ .Values.backend.name }} + port: + number: {{ .Values.backend.service.externalPort.number | int }} + tls: + - secretName: zarr-fuse-dashboard-dyn-cloud-e-infra-cz-tls + hosts: + - zarr-fuse-dashboard.dyn.cloud.e-infra.cz diff --git a/app/databuk/dashboard/charts/dashboard/values.yaml b/app/databuk/dashboard/charts/dashboard/values.yaml new file mode 100644 index 00000000..2d5f179a --- /dev/null +++ b/app/databuk/dashboard/charts/dashboard/values.yaml @@ -0,0 +1,106 @@ +# -- Registry details +# @default -- ~ +registry: + # -- Host of the registry + host: docker.io + +backend: + name: dashboard-backend + replicaCount: 1 + image: + name: jbrezmorf/zarr-fuse-dashboard-backend + tag: latest + pullPolicy: Always + + service: + type: ClusterIP + externalPort: + number: 8000 + name: http + internalPort: + number: 8000 + name: http + + secrets: + s3: + accessKey: "your-access-key" + secretKey: "your-secret-key" + + resources: + requests: + cpu: 125m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + + securityContext: + pod: + fsGroup: 11233 + runAsNonRoot: true + runAsUser: 11233 + runAsGroup: 11233 + container: + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: true + runAsUser: 11233 + runAsGroup: 11233 + seccompProfile: + type: RuntimeDefault + +frontend: + name: dashboard-frontend + replicaCount: 1 + image: + name: jbrezmorf/zarr-fuse-dashboard-frontend + tag: latest + pullPolicy: Always + + service: + type: ClusterIP + externalPort: + number: 8091 + name: http + internalPort: + number: 8091 + name: http + + resources: + requests: + cpu: 125m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + + securityContext: + pod: + fsGroup: 11233 + runAsNonRoot: true + runAsUser: 11233 + runAsGroup: 11233 + container: + capabilities: + drop: + - ALL + allowPrivilegeEscalation: false + privileged: false + runAsNonRoot: true + runAsUser: 11233 + runAsGroup: 11233 + seccompProfile: + type: RuntimeDefault + +# -- Ingress configuration +# @default -- ~ +ingress: + # -- Ingress class name + className: "nginx" + # -- Ingress annotations + annotations: + kubernetes.io/tls-acme: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" diff --git a/app/databuk/dashboard/oci/Containerfile.backend b/app/databuk/dashboard/oci/Containerfile.backend new file mode 100644 index 00000000..be149e9a --- /dev/null +++ b/app/databuk/dashboard/oci/Containerfile.backend @@ -0,0 +1,36 @@ +FROM docker.io/library/python:3.11-slim-bullseye + +ARG APP_VERSION="devel" + +LABEL org.opencontainers.image.title="ZarrFuse Dashboard - Backend" +LABEL org.opencontainers.image.description="Backend for the ZarrFuse Dashboard." +LABEL org.opencontainers.image.version="${APP_VERSION}" +LABEL org.opencontainers.image.licenses="BSD-3-Clause" +LABEL org.opencontainers.image.source="https://github.com/geomop/zarr_fuse" +LABEL org.opencontainers.image.url="https://github.com/geomop/zarr_fuse" +LABEL org.opencontainers.image.authors="Geomop / Stepan Moc " +LABEL maintainer="Geomop / Stepan Moc " + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PIP_NO_CACHE_DIR=1 + +RUN groupadd -r dashboard && useradd -r -g dashboard -u 11233 -m -d /app dashboard + +# --- Install dashboard server --- +WORKDIR /app + +COPY --chown=dashboard:dashboard backend/ /app/ + +RUN python -m pip install --upgrade pip && \ + pip install . + +RUN pip install --no-cache-dir /app + +RUN chown -R dashboard:dashboard /app + +USER dashboard +WORKDIR /app +EXPOSE 8000 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/app/databuk/dashboard/oci/Containerfile.frontend b/app/databuk/dashboard/oci/Containerfile.frontend new file mode 100644 index 00000000..150d4f5d --- /dev/null +++ b/app/databuk/dashboard/oci/Containerfile.frontend @@ -0,0 +1,29 @@ +FROM docker.io/library/alpine:3.22.1 + +ARG APP_VERSION="devel" + +LABEL org.opencontainers.image.title="ZarrFuse Dashboard - Frontend" +LABEL org.opencontainers.image.description="Frontend for the ZarrFuse Dashboard." +LABEL org.opencontainers.image.version="${APP_VERSION}" +LABEL org.opencontainers.image.licenses="BSD-3-Clause" +LABEL org.opencontainers.image.source="https://github.com/geomop/zarr_fuse" +LABEL org.opencontainers.image.url="https://github.com/geomop/zarr_fuse" +LABEL org.opencontainers.image.authors="Geomop / Stepan Moc " +LABEL maintainer="Geomop / Stepan Moc " + +RUN addgroup -S nginx && adduser -S -D -H -h /nginx -u 11233 -G nginx nginx + +RUN apk add --no-cache nginx curl && \ + mkdir -p /var/www/html + +COPY --chown=nginx:nginx dist/ /var/www/html +COPY --chown=nginx:nginx oci/nginx.conf /etc/nginx + +RUN ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log + +EXPOSE 8091 + +USER nginx + +CMD [ "nginx", "-g", "daemon off;" ] diff --git a/app/databuk/dashboard/oci/nginx.conf b/app/databuk/dashboard/oci/nginx.conf new file mode 100644 index 00000000..f6ad44ca --- /dev/null +++ b/app/databuk/dashboard/oci/nginx.conf @@ -0,0 +1,41 @@ +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /tmp/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + + sendfile on; + keepalive_timeout 65; + + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + + server { + listen 8091; + server_name _; + + root /var/www/html; + index index.html; + + location / { + try_files $uri /index.html; + } + + location ~* \.(?:js|mjs|css|woff2?|ttf|eot|ico|png|jpg|jpeg|gif|svg)$ { + try_files $uri =404; + add_header Cache-Control "public, max-age=31536000, immutable"; + } + } +} diff --git a/app/databuk/dashboard/src/App.tsx b/app/databuk/dashboard/src/App.tsx index a12b66b6..75bc54b8 100644 --- a/app/databuk/dashboard/src/App.tsx +++ b/app/databuk/dashboard/src/App.tsx @@ -1,6 +1,7 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { Sidebar } from './components/sidebar'; +import React, { useEffect, useRef, useState } from 'react'; +import { API_BASE_URL } from './api'; import LogPanel from './components/LogPanel'; +import { Sidebar } from './components/sidebar'; import type { ConfigData } from './components/sidebar/types/sidebar'; function App() { @@ -10,12 +11,12 @@ function App() { const [configData, setConfigData] = useState(null); const [configLoading, setConfigLoading] = useState(true); const [configError, setConfigError] = useState(null); - const [selectedNode, setSelectedNode] = useState<{storeName: string, nodePath: string} | null>(null); + const [selectedNode, setSelectedNode] = useState<{ storeName: string, nodePath: string } | null>(null); const [nodeDetails, setNodeDetails] = useState(null); const [nodeLoading, setNodeLoading] = useState(false); const [nodeError, setNodeError] = useState(null); const [expandedVariables, setExpandedVariables] = useState>(new Set()); - const [variableData, setVariableData] = useState<{[key: string]: any}>({}); + const [variableData, setVariableData] = useState<{ [key: string]: any }>({}); const [showLogPanel, setShowLogPanel] = useState(false); const sidebarRef = useRef(null); @@ -24,7 +25,7 @@ function App() { const fetchConfig = async () => { try { setConfigLoading(true); - const response = await fetch('http://localhost:8000/api/config/current'); + const response = await fetch(`${API_BASE_URL}/api/config/current`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } @@ -52,9 +53,9 @@ function App() { setSelectedNode({ storeName, nodePath }); setNodeLoading(true); setNodeError(null); - + try { - const response = await fetch(`http://localhost:8000/api/s3/node/${storeName}/${nodePath}`); + const response = await fetch(`${API_BASE_URL}/api/s3/node/${storeName}/${nodePath}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } @@ -75,7 +76,7 @@ function App() { useEffect(() => { const handleMouseMove = (e: MouseEvent) => { if (!isResizing) return; - + const newWidth = e.clientX; if (newWidth >= 200 && newWidth <= 600) { setSidebarWidth(newWidth); @@ -100,7 +101,7 @@ function App() { // Handle variable click - toggle expand/collapse const handleVariableClick = async (variableName: string, variablePath: string) => { if (!selectedNode) return; - + // Toggle expanded state const newExpanded = new Set(expandedVariables); if (newExpanded.has(variableName)) { @@ -111,36 +112,38 @@ function App() { newExpanded.add(variableName); setExpandedVariables(newExpanded); } - + // If data already loaded, just show it if (variableData[variableName] && !variableData[variableName].loading) { return; } - + console.log(`🔍 Clicked variable: ${variableName} at ${variablePath}`); - + try { setVariableData(prev => ({ ...prev, [variableName]: { loading: true } })); - - // Call backend to get variable data + + // Call backend to get variable data console.log(`🔍 Fetching: /api/s3/variable/${selectedNode.storeName}/${variablePath}`); - const response = await fetch(`http://localhost:8000/api/s3/variable/${selectedNode.storeName}/${variablePath}`); - + const response = await fetch(`${API_BASE_URL}/api/s3/variable/${selectedNode.storeName}/${variablePath}`); + if (!response.ok) { throw new Error(`Failed to fetch variable data: ${response.statusText}`); } - + const data = await response.json(); console.log('Variable data:', data); - + setVariableData(prev => ({ ...prev, [variableName]: data })); } catch (error) { console.error('Error loading variable data:', error); - setVariableData(prev => ({ ...prev, [variableName]: { - name: variableName, - path: variablePath, - error: `Failed to load data: ${error instanceof Error ? error.message : 'Unknown error'}` - }})); + setVariableData(prev => ({ + ...prev, [variableName]: { + name: variableName, + path: variablePath, + error: `Failed to load data: ${error instanceof Error ? error.message : 'Unknown error'}` + } + })); } }; @@ -148,26 +151,26 @@ function App() {
{/* Sidebar */} {isVisible && ( -
- setIsVisible(false)} + setIsVisible(false)} configData={configData} configLoading={configLoading} configError={configError} onNodeClick={handleNodeClick} onLogClick={handleLogClick} /> - + {/* Resize Handle */}
-
+
)} {/* Content Area - Main Panel */} @@ -182,9 +185,9 @@ function App() { - + )} - + {/* Node Details Content */} {selectedNode ? (
@@ -196,8 +199,8 @@ function App() {

xarray DataSet • {selectedNode.storeName} -

-
+

+
{/* Loading State */} @@ -279,11 +282,11 @@ function App() { {Object.entries(nodeDetails.vars).map(([name, variable]: [string, any]) => { const isExpanded = expandedVariables.has(name); const data = variableData[name]; - + return (
{/* Variable Header - Clickable */} -
handleVariableClick(name, variable.path || `${selectedNode?.nodePath ? selectedNode.nodePath + '/' : ''}${name}`)} > @@ -299,7 +302,7 @@ function App() { {isExpanded ? 'Click to collapse' : 'Click to expand'}
- + {/* Variable Details - Expandable */} {isExpanded && (
@@ -309,13 +312,13 @@ function App() {

Loading variable data...

)} - + {data?.error && (

Error: {data.error}

)} - + {data && !data.loading && !data.error && (

Path: {data.path}

@@ -362,9 +365,9 @@ function App() { {/* Log Panel */} - setShowLogPanel(false)} + setShowLogPanel(false)} />
); diff --git a/app/databuk/dashboard/src/api.ts b/app/databuk/dashboard/src/api.ts new file mode 100644 index 00000000..3b1f873b --- /dev/null +++ b/app/databuk/dashboard/src/api.ts @@ -0,0 +1 @@ +export const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; diff --git a/app/databuk/dashboard/src/components/LogPanel.tsx b/app/databuk/dashboard/src/components/LogPanel.tsx index f7e93562..8d63d522 100644 --- a/app/databuk/dashboard/src/components/LogPanel.tsx +++ b/app/databuk/dashboard/src/components/LogPanel.tsx @@ -1,5 +1,6 @@ -import React, { useState, useEffect } from 'react'; -import { X, AlertCircle, CheckCircle, Clock, RefreshCw } from 'lucide-react'; +import { AlertCircle, CheckCircle, Clock, RefreshCw, X } from 'lucide-react'; +import React, { useEffect, useState } from 'react'; +import { API_BASE_URL } from '../api'; interface LogEntry { id: string; @@ -18,7 +19,7 @@ interface LogPanelProps { const LogPanel: React.FC = ({ show, onClose }) => { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(false); - const [filter, setFilter] = useState<'backend'>('backend'); + const [filter] = useState<'backend'>('backend'); // No mock data; will be populated from API later @@ -26,7 +27,7 @@ const LogPanel: React.FC = ({ show, onClose }) => { if (show) { setLoading(true); // Fetch logs (errors and warnings only) from backend - fetch('http://localhost:8000/api/logs') + fetch(`${API_BASE_URL}/api/logs`) .then(async (res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); @@ -49,13 +50,7 @@ const LogPanel: React.FC = ({ show, onClose }) => { } }, [show]); - const filteredLogs = logs; - - const handleResolve = (logId: string) => { - setLogs(prev => prev.map(log => - log.id === logId ? { ...log, resolved: true } : log - )); - }; + const filteredLogs = logs.filter(l => l.category === filter); const getLevelIcon = (level: string) => { switch (level) { @@ -84,11 +79,11 @@ const LogPanel: React.FC = ({ show, onClose }) => { return (
{/* Overlay */} -
- + {/* Log Panel */}
{/* Header */} @@ -138,9 +133,8 @@ const LogPanel: React.FC = ({ show, onClose }) => { filteredLogs.map((log) => (
@@ -162,7 +156,7 @@ const LogPanel: React.FC = ({ show, onClose }) => {

{log.message}

- + {/* No resolve action for backend-only logs */}
diff --git a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx index 16d28c06..5142edb6 100644 --- a/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx +++ b/app/databuk/dashboard/src/components/sidebar/Sidebar.tsx @@ -1,5 +1,6 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { X, Database, BarChart3, AlertCircle, Clock, RefreshCw, Folder, ChevronRight, ChevronDown } from 'lucide-react'; +import { AlertCircle, BarChart3, ChevronDown, ChevronRight, Clock, Database, Folder, RefreshCw, X } from 'lucide-react'; +import React, { useCallback, useEffect, useState } from 'react'; +import { API_BASE_URL } from '../../api'; import type { SidebarProps } from './types/sidebar'; // Types for S3 data @@ -23,7 +24,7 @@ interface S3Response { structure: S3Structure; } -const Sidebar: React.FC = ({ +const Sidebar: React.FC = ({ onClose, configData, configLoading, @@ -31,7 +32,7 @@ const Sidebar: React.FC = ({ onNodeClick, onLogClick }) => { - + // S3 data state const [s3Data, setS3Data] = useState(null); const [s3Loading, setS3Loading] = useState(false); @@ -58,7 +59,7 @@ const Sidebar: React.FC = ({ setS3Loading(true); setS3Error(null); try { - const response = await fetch('http://localhost:8000/api/s3/structure'); + const response = await fetch(`${API_BASE_URL}/api/s3/structure`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } @@ -106,15 +107,15 @@ const Sidebar: React.FC = ({ const renderTreeItem = (item: any, level: number = 0, parentPath: string = '', storeName: string = '') => { const indent = level * 16; const itemPath = parentPath ? `${parentPath}/${item.name}` : item.name; - + console.log('Rendering item:', item); // Debug log - + if (item.type === 'group') { const groupsOnly = (item.children || []).filter((c: any) => c.type === 'group'); const isExpanded = expandedPaths.has(itemPath); return (
-
onNodeClick?.(storeName, itemPath)} > @@ -178,7 +179,7 @@ const Sidebar: React.FC = ({
- + {/* Store URL and Description */} {configData && (
@@ -199,7 +200,7 @@ const Sidebar: React.FC = ({ {s3Loading ? 'Loading' : (s3Error || hasStoreError) ? 'Issues' : 'Active'}
-
{ fetchS3Data(); @@ -264,7 +265,7 @@ const Sidebar: React.FC = ({

Available data sources

- + {/* S3 Loading State */} {s3Loading && (
@@ -286,14 +287,14 @@ const Sidebar: React.FC = ({
{s3Data.structure.stores.map((store) => (
-
onNodeClick?.(store.name, '')} > {store.name} (click to view root variables)
- + {store.error ? (
{store.error}
) : store.structure ? ( @@ -317,7 +318,7 @@ const Sidebar: React.FC = ({
{/* Fixed Footer - Logs */}
-
@@ -330,5 +331,3 @@ const Sidebar: React.FC = ({ }; export default Sidebar; - - diff --git a/app/databuk/dashboard/vite.config.ts b/app/databuk/dashboard/vite.config.ts index 2328e170..d696d81c 100644 --- a/app/databuk/dashboard/vite.config.ts +++ b/app/databuk/dashboard/vite.config.ts @@ -1,5 +1,5 @@ -import { defineConfig } from 'vite' import react from '@vitejs/plugin-react-swc' +import { defineConfig } from 'vite' // https://vite.dev/config/ export default defineConfig({ diff --git a/app/databuk/ingress_server/charts/ingress-server/templates/deployment.yaml b/app/databuk/ingress_server/charts/ingress-server/templates/deployment.yaml index b1246e01..bef23604 100644 --- a/app/databuk/ingress_server/charts/ingress-server/templates/deployment.yaml +++ b/app/databuk/ingress_server/charts/ingress-server/templates/deployment.yaml @@ -35,19 +35,9 @@ spec: {{- toYaml .Values.resources | nindent 12 }} securityContext: {{- toYaml .Values.securityContext.container | nindent 12 }} - env: - - name: S3_ENDPOINT_URL - value: {{ required "S3_ENDPOINT_URL needs to be set" .Values.deployment.secrets.s3.endpointUrl | quote }} - - name: S3_ACCESS_KEY - value: {{ required "S3_ACCESS_KEY needs to be set" .Values.deployment.secrets.s3.accessKey | quote }} - - name: S3_SECRET_KEY - value: {{ required "S3_SECRET_KEY needs to be set" .Values.deployment.secrets.s3.secretKey | quote }} - - name: S3_STORE_URL - value: {{ required "S3_STORE_URL needs to be set" .Values.deployment.secrets.s3.storeUrl | quote }} - - name: BASIC_AUTH_USERS_JSON - value: {{ required "BASIC_AUTH_USERS_JSON needs to be set" .Values.deployment.secrets.basicAuth.usersJson | toJson | quote }} - - name: QUEUE_DIR - value: {{ .Values.deployment.queue.path | quote }} + envFrom: + - secretRef: + name: {{ .Values.app.name }}-env-secrets volumeMounts: - name: ingress-server-tmp mountPath: /tmp diff --git a/app/databuk/ingress_server/charts/ingress-server/templates/secret.yaml b/app/databuk/ingress_server/charts/ingress-server/templates/secret.yaml new file mode 100644 index 00000000..8ad540bc --- /dev/null +++ b/app/databuk/ingress_server/charts/ingress-server/templates/secret.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.app.name }}-env-secrets + labels: + app.kubernetes.io/name: {{ .Values.app.name }} +type: Opaque +stringData: + S3_ENDPOINT_URL: {{ required "S3_ENDPOINT_URL needs to be set" .Values.deployment.secrets.s3.endpointUrl | quote }} + S3_ACCESS_KEY: {{ required "S3_ACCESS_KEY needs to be set" .Values.deployment.secrets.s3.accessKey | quote }} + S3_SECRET_KEY: {{ required "S3_SECRET_KEY needs to be set" .Values.deployment.secrets.s3.secretKey | quote }} + S3_STORE_URL: {{ required "S3_STORE_URL needs to be set" .Values.deployment.secrets.s3.storeUrl | quote }} + BASIC_AUTH_USERS_JSON: {{ required "BASIC_AUTH_USERS_JSON needs to be set" .Values.deployment.secrets.basicAuth.usersJson | toJson | quote }} + QUEUE_DIR: {{ .Values.deployment.queue.path | quote }} diff --git a/app/databuk/ingress_server/src/inputs/endpoints_config.yaml b/app/databuk/ingress_server/src/inputs/endpoints_config.yaml index e9124013..bb5241d6 100644 --- a/app/databuk/ingress_server/src/inputs/endpoints_config.yaml +++ b/app/databuk/ingress_server/src/inputs/endpoints_config.yaml @@ -2,3 +2,9 @@ endpoints: - name: bukov endpoint: /api/v1/bukov schema_path: schemas/bukov_schema_v1.yaml + +active-scrappers: + - name: bukov + cron: "*/5 * * * *" + url: http://test.cz/api/v1/bukov + schema_path: schemas/bukov_schema_v1.yaml From a18db836f92afcad9e5d70db5736e0250ab02d51 Mon Sep 17 00:00:00 2001 From: mfatihakbas <119815601+mfatihakbas@users.noreply.github.com> Date: Tue, 7 Oct 2025 00:54:33 +0300 Subject: [PATCH 11/12] Add zarr_fuse integration flag system - Add Use_zarr_fuse flag to endpoints.yaml and ConfigManager - Add zarr_fuse import and path resolution in S3Service - Add flag-controlled routing for get_variable_data and get_store_structure - Fix double /api prefix issue in frontend requests --- app/databuk/dashboard/.env.development | 2 +- .../dashboard/backend/config/endpoints.yaml | 5 ++- .../dashboard/backend/core/config_manager.py | 1 + .../dashboard/backend/services/s3_service.py | 45 +++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/app/databuk/dashboard/.env.development b/app/databuk/dashboard/.env.development index b0e8fc8b..5934e2e7 100644 --- a/app/databuk/dashboard/.env.development +++ b/app/databuk/dashboard/.env.development @@ -1 +1 @@ -VITE_API_URL=http://localhost:8000/api +VITE_API_URL=http://localhost:8000 diff --git a/app/databuk/dashboard/backend/config/endpoints.yaml b/app/databuk/dashboard/backend/config/endpoints.yaml index 050b6775..adce7727 100644 --- a/app/databuk/dashboard/backend/config/endpoints.yaml +++ b/app/databuk/dashboard/backend/config/endpoints.yaml @@ -5,11 +5,14 @@ # Reload settings Reload_interval: 300 # 5 minutes in seconds + # Feature flag for zarr_fuse integration + Use_zarr_fuse: true + # Schema configuration Schema_file: "schemas/test_schema.yaml" # S3 Store configuration - Using new credentials - STORE_URL: "s3://${S3_BUCKET_NAME}/bukov.zarr" + STORE_URL: "s3://${S3_BUCKET_NAME}/dashboard-test/structure_tree.zarr" S3_ENDPOINT_URL: "${S3_ENDPOINT_URL}" S3_access_key: "${S3_ACCESS_KEY}" diff --git a/app/databuk/dashboard/backend/core/config_manager.py b/app/databuk/dashboard/backend/core/config_manager.py index a726faab..47f7441c 100644 --- a/app/databuk/dashboard/backend/core/config_manager.py +++ b/app/databuk/dashboard/backend/core/config_manager.py @@ -18,6 +18,7 @@ class EndpointConfig(BaseModel): Description: str = Field(..., description="Endpoint description") Store_type: str = Field(default="zarr", description="Store type") Version: str = Field(default="1.0.0", description="Version") + Use_zarr_fuse: bool = Field(default=False, description="Enable zarr_fuse integration") class ConfigManager: """Manages YAML configuration for S3 endpoints""" diff --git a/app/databuk/dashboard/backend/services/s3_service.py b/app/databuk/dashboard/backend/services/s3_service.py index f75d9fb4..304f6a9d 100644 --- a/app/databuk/dashboard/backend/services/s3_service.py +++ b/app/databuk/dashboard/backend/services/s3_service.py @@ -6,6 +6,21 @@ from core.config_manager import config_manager, EndpointConfig import fsspec +# zarr_fuse integration - simplified approach +ZARR_FUSE_AVAILABLE = False +try: + # Add project root to Python path + import sys + from pathlib import Path + project_root = Path(__file__).parent.parent.parent.parent.parent.parent + sys.path.insert(0, str(project_root)) + + import zarr_fuse + ZARR_FUSE_AVAILABLE = True +except ImportError as e: + ZARR_FUSE_AVAILABLE = False + print(f"DEBUG: zarr_fuse import failed: {e}") + logger = logging.getLogger(__name__) def clean_nan_values(data): @@ -254,6 +269,16 @@ def get_store_structure(self) -> Dict[str, Any]: if not self._fs: raise ValueError("Not connected to S3") + # Check if zarr_fuse integration is enabled + use_zarr_fuse = getattr(self._current_config, 'Use_zarr_fuse', False) + if use_zarr_fuse: + if ZARR_FUSE_AVAILABLE: + print(f"Using zarr_fuse path for structure") + return self._get_store_structure_zarr_fuse() + else: + raise Exception("zarr_fuse integration enabled but library not available") + + # LEGACY CODE BELOW - NO CHANGES TO EXISTING LOGIC try: # Extract store path from STORE_URL store_url = self._current_config.STORE_URL @@ -312,6 +337,11 @@ def get_store_structure(self) -> Dict[str, Any]: logger.error(f"Failed to get store structure: {e}") raise ValueError(f"Failed to get store structure: {e}") + def _get_store_structure_zarr_fuse(self) -> Dict[str, Any]: + """New implementation using zarr_fuse library""" + # TODO: Implement zarr_fuse integration + raise NotImplementedError("zarr_fuse structure integration not yet implemented") + def _extract_structure(self, store_path: str) -> Dict[str, Any]: """Extract structure from a Zarr store - refactored and simplified""" try: @@ -531,6 +561,16 @@ def get_variable_data(self, store_name: str, variable_path: str) -> Dict[str, An if not self._current_config: raise Exception("No endpoint configuration found") + # Check if zarr_fuse integration is enabled + use_zarr_fuse = getattr(self._current_config, 'Use_zarr_fuse', False) + if use_zarr_fuse: + if ZARR_FUSE_AVAILABLE: + print(f"Using zarr_fuse path for variable: {variable_path}") + return self._get_variable_data_zarr_fuse(store_name, variable_path) + else: + raise Exception("zarr_fuse integration enabled but library not available") + + # LEGACY CODE BELOW - NO CHANGES TO EXISTING LOGIC # Get store configuration store_url = self._current_config.STORE_URL store_path = store_url[5:] if store_url.startswith('s3://') else store_url @@ -632,6 +672,11 @@ def get_variable_data(self, store_name: str, variable_path: str) -> Dict[str, An print(f"ERROR: Error getting variable data: {e}") raise + def _get_variable_data_zarr_fuse(self, store_name: str, variable_path: str) -> Dict[str, Any]: + """New implementation using zarr_fuse library""" + # TODO: Implement zarr_fuse integration + raise NotImplementedError("zarr_fuse integration not yet implemented") + def _extract_coordinate_info(self, coord_group, name: str) -> Dict[str, Any]: """Extract information about a coordinate""" try: From c76d2ca0ecc4460e378a8be562a50574befd8411 Mon Sep 17 00:00:00 2001 From: mfatihakbas <119815601+mfatihakbas@users.noreply.github.com> Date: Tue, 7 Oct 2025 01:17:34 +0300 Subject: [PATCH 12/12] Implement get_store_structure with zarr_fuse and xarray - Add _get_store_structure_zarr_fuse using zarr_fuse's internal store opening - Switch to xarray.open_zarr due to FsspecStore compatibility issues - Add _convert_to_legacy_format to match frontend expectations - Fix S3 endpoint URL mapping for proper connection --- .../dashboard/backend/services/s3_service.py | 281 +++++++++++++++++- 1 file changed, 278 insertions(+), 3 deletions(-) diff --git a/app/databuk/dashboard/backend/services/s3_service.py b/app/databuk/dashboard/backend/services/s3_service.py index 304f6a9d..a1cde87e 100644 --- a/app/databuk/dashboard/backend/services/s3_service.py +++ b/app/databuk/dashboard/backend/services/s3_service.py @@ -339,8 +339,268 @@ def get_store_structure(self) -> Dict[str, Any]: def _get_store_structure_zarr_fuse(self) -> Dict[str, Any]: """New implementation using zarr_fuse library""" - # TODO: Implement zarr_fuse integration - raise NotImplementedError("zarr_fuse structure integration not yet implemented") + try: + # Get store configuration + store_url = self._current_config.STORE_URL + store_path = store_url[5:] if store_url.startswith('s3://') else store_url + store_name = store_path.split('/')[-1] + + print(f"Opening store with zarr_fuse: {store_path}") + + # Create storage options for zarr_fuse + storage_options = self._get_storage_options() + + # Create zarr store directly using zarr_fuse's internal functions + # Convert dashboard config to zarr_fuse format + zf_options = { + 'STORE_URL': store_url, + 'S3_ENDPOINT_URL': self._current_config.S3_ENDPOINT_URL, # Use config directly + 'S3_ACCESS_KEY': self._current_config.S3_access_key, # Use config directly + 'S3_SECRET_KEY': self._current_config.S3_secret_key, # Use config directly + 'S3_OPTIONS': '{}' # Empty JSON for custom options + } + + print(f"DEBUG: zf_options = {zf_options}") + + # Open zarr store using zarr_fuse's internal function + zarr_store = zarr_fuse.zarr_storage._zarr_store_open(zf_options) + + # Use direct xarray approach instead of Node for now + # This avoids the FsspecStore.get() prototype issue + try: + # Open the root dataset directly + import xarray as xr + ds = xr.open_zarr(zarr_store) + structure = self._build_structure_from_dataset(ds, "") + except Exception as e: + print(f"Warning: Could not open root dataset, trying group approach: {e}") + # Fallback: try to open as group and traverse + import zarr + group = zarr.open_group(zarr_store, mode='r') + structure = self._build_structure_from_zarr_group(group, "") + + # Convert zarr_fuse structure to legacy format + legacy_structure = self._convert_to_legacy_format(structure) + + # Format response to match legacy format + zarr_stores = [{ + 'name': store_name, + 'path': store_path, + 'type': 'zarr_store', + 'structure': legacy_structure + }] + + # Debug: print structure to see what we're returning + print(f"DEBUG: zarr_fuse structure = {structure}") + import json + print(f"DEBUG: structure JSON = {json.dumps(structure, indent=2, default=str)}") + + bucket_name = store_path.split('/')[0] + + return { + 'status': 'success', + 'bucket_name': bucket_name, + 'store_url': store_url, + 'total_stores': len(zarr_stores), + 'stores': zarr_stores + } + + except Exception as e: + logger.error(f"Failed to get store structure with zarr_fuse: {e}") + raise ValueError(f"Failed to get store structure with zarr_fuse: {e}") + + def _build_structure_from_node(self, node: 'zarr_fuse.Node') -> Dict[str, Any]: + """Build structure dictionary from zarr_fuse Node""" + structure = {} + + # Add current node's dataset info + try: + ds = node.dataset + if ds: + # Add variables + if ds.data_vars: + structure['vars'] = {} + for var_name, var in ds.data_vars.items(): + structure['vars'][var_name] = { + 'dims': list(var.dims), + 'shape': list(var.shape), + 'dtype': str(var.dtype), + 'attrs': dict(var.attrs) + } + + # Add coordinates + if ds.coords: + structure['coords'] = {} + for coord_name, coord in ds.coords.items(): + structure['coords'][coord_name] = { + 'dims': list(coord.dims), + 'shape': list(coord.shape), + 'dtype': str(coord.dtype), + 'attrs': dict(coord.attrs) + } + + # Add global attributes + if ds.attrs: + structure['attrs'] = dict(ds.attrs) + except Exception as e: + print(f"Warning: Could not read dataset for node {node.name}: {e}") + + # Add children (subgroups) + if hasattr(node, 'children') and node.children: + structure['groups'] = {} + for child_name, child_node in node.children.items(): + structure['groups'][child_name] = self._build_structure_from_node(child_node) + + return structure + + def _build_structure_from_dataset(self, ds: 'xr.Dataset', group_path: str) -> Dict[str, Any]: + """Build structure dictionary from xarray Dataset""" + structure = {} + + # Add variables + if ds.data_vars: + structure['vars'] = {} + for var_name, var in ds.data_vars.items(): + structure['vars'][var_name] = { + 'dims': list(var.dims), + 'shape': list(var.shape), + 'dtype': str(var.dtype), + 'attrs': dict(var.attrs) + } + + # Add coordinates + if ds.coords: + structure['coords'] = {} + for coord_name, coord in ds.coords.items(): + structure['coords'][coord_name] = { + 'dims': list(coord.dims), + 'shape': list(coord.shape), + 'dtype': str(coord.dtype), + 'attrs': dict(coord.attrs) + } + + # Add global attributes + if ds.attrs: + structure['attrs'] = dict(ds.attrs) + + return structure + + def _build_structure_from_zarr_group(self, group: 'zarr.Group', group_path: str) -> Dict[str, Any]: + """Build structure dictionary from zarr Group""" + structure = {} + + # Add arrays (variables) + arrays = list(group.arrays()) + if arrays: + structure['vars'] = {} + for name, array in arrays: + structure['vars'][name] = { + 'dims': list(array.shape), # Simplified - zarr doesn't have dim names + 'shape': list(array.shape), + 'dtype': str(array.dtype), + 'attrs': dict(array.attrs) + } + + # Add subgroups + subgroups = list(group.groups()) + if subgroups: + structure['groups'] = {} + for name, subgroup in subgroups: + structure['groups'][name] = self._build_structure_from_zarr_group(subgroup, f"{group_path}/{name}") + + # Add group attributes + if group.attrs: + structure['attrs'] = dict(group.attrs) + + return structure + + def _convert_to_legacy_format(self, zf_structure: Dict[str, Any]) -> Dict[str, Any]: + """Convert zarr_fuse structure format to legacy dashboard format""" + legacy = { + 'name': 'root', + 'path': '', + 'type': 'group', + 'children': [] + } + + # Add variables as arrays + if 'vars' in zf_structure: + for var_name, var_info in zf_structure['vars'].items(): + legacy['children'].append({ + 'name': var_name, + 'path': var_name, + 'type': 'array', + 'shape': var_info['shape'], + 'dtype': var_info['dtype'], + 'chunks': var_info['shape'], # Simplified + 'size': var_info['shape'][0] if var_info['shape'] else 0, + 'sample_data': [] # Will be filled by legacy code + }) + + # Add groups recursively + if 'groups' in zf_structure: + for group_name, group_info in zf_structure['groups'].items(): + group_legacy = { + 'name': group_name, + 'path': group_name, + 'type': 'group', + 'children': [] + } + + # Add group variables + if 'vars' in group_info: + for var_name, var_info in group_info['vars'].items(): + group_legacy['children'].append({ + 'name': var_name, + 'path': f"{group_name}/{var_name}", + 'type': 'array', + 'shape': var_info['shape'], + 'dtype': var_info['dtype'], + 'chunks': var_info['shape'], + 'size': var_info['shape'][0] if var_info['shape'] else 0, + 'sample_data': [] + }) + + # Add subgroups recursively + if 'groups' in group_info: + for subgroup_name, subgroup_info in group_info['groups'].items(): + subgroup_legacy = self._convert_group_to_legacy(subgroup_name, f"{group_name}/{subgroup_name}", subgroup_info) + group_legacy['children'].append(subgroup_legacy) + + legacy['children'].append(group_legacy) + + return legacy + + def _convert_group_to_legacy(self, group_name: str, group_path: str, group_info: Dict[str, Any]) -> Dict[str, Any]: + """Convert a single group to legacy format""" + group_legacy = { + 'name': group_name, + 'path': group_path, + 'type': 'group', + 'children': [] + } + + # Add variables + if 'vars' in group_info: + for var_name, var_info in group_info['vars'].items(): + group_legacy['children'].append({ + 'name': var_name, + 'path': f"{group_path}/{var_name}", + 'type': 'array', + 'shape': var_info['shape'], + 'dtype': var_info['dtype'], + 'chunks': var_info['shape'], + 'size': var_info['shape'][0] if var_info['shape'] else 0, + 'sample_data': [] + }) + + # Add subgroups recursively + if 'groups' in group_info: + for subgroup_name, subgroup_info in group_info['groups'].items(): + subgroup_legacy = self._convert_group_to_legacy(subgroup_name, f"{group_path}/{subgroup_name}", subgroup_info) + group_legacy['children'].append(subgroup_legacy) + + return group_legacy def _extract_structure(self, store_path: str) -> Dict[str, Any]: """Extract structure from a Zarr store - refactored and simplified""" @@ -391,6 +651,16 @@ def get_node_details(self, store_name: str, node_path: str) -> Dict[str, Any]: if not self._fs or not self._current_config: raise ValueError("Not connected to S3. Call connect() first.") + # Check if zarr_fuse integration is enabled + use_zarr_fuse = getattr(self._current_config, 'Use_zarr_fuse', False) + if use_zarr_fuse: + if ZARR_FUSE_AVAILABLE: + print(f"Using zarr_fuse path for node details: {node_path}") + return self._get_node_details_zarr_fuse(store_name, node_path) + else: + raise Exception("zarr_fuse integration enabled but library not available") + + # LEGACY CODE BELOW - NO CHANGES TO EXISTING LOGIC try: print(f"Getting details for node: {store_name}/{node_path}") @@ -548,7 +818,12 @@ def get_node_details(self, store_name: str, node_path: str) -> Dict[str, Any]: except Exception as e: logger.error(f"Failed to get node details for {store_name}/{node_path}: {e}") raise - + + def _get_node_details_zarr_fuse(self, store_name: str, node_path: str) -> Dict[str, Any]: + """New implementation using zarr_fuse library""" + # TODO: Implement zarr_fuse integration + raise NotImplementedError("zarr_fuse node details integration not yet implemented") + def get_variable_data(self, store_name: str, variable_path: str) -> Dict[str, Any]: """Get actual data for a specific variable (limited sample)""" try: