From 9d914374df0918e306a500a25f5f7965ccf3ff6a Mon Sep 17 00:00:00 2001 From: Jackson Chen <90215880+Sma1lboy@users.noreply.github.com> Date: Thu, 28 Nov 2024 10:07:21 -0600 Subject: [PATCH 01/40] feat(backent): adding react-ts template (#56) ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced a new template for React applications using TypeScript and Vite, including essential setup files. - Added a main HTML entry point for the application. - Configured ESLint for improved code quality and best practices in TypeScript and React environments. - **Documentation** - Added a README file outlining setup instructions and recommended configurations for the template. - **Configuration** - New TypeScript and Vite configuration files created to optimize the development environment. --- backend/template/react-ts/.gitignore | 24 + backend/template/react-ts/README.md | 50 + backend/template/react-ts/eslint.config.js | 28 + backend/template/react-ts/index.html | 13 + backend/template/react-ts/package-lock.json | 3041 ++++++++++++++++++ backend/template/react-ts/package.json | 30 + backend/template/react-ts/public/codefox.svg | 29 + backend/template/react-ts/src/index.jsx | 6 + backend/template/react-ts/src/vite-env.d.ts | 1 + backend/template/react-ts/tsconfig.app.json | 26 + backend/template/react-ts/tsconfig.json | 7 + backend/template/react-ts/tsconfig.node.json | 24 + backend/template/react-ts/vite.config.ts | 7 + pnpm-lock.yaml | 9 +- 14 files changed, 3291 insertions(+), 4 deletions(-) create mode 100644 backend/template/react-ts/.gitignore create mode 100644 backend/template/react-ts/README.md create mode 100644 backend/template/react-ts/eslint.config.js create mode 100644 backend/template/react-ts/index.html create mode 100644 backend/template/react-ts/package-lock.json create mode 100644 backend/template/react-ts/package.json create mode 100644 backend/template/react-ts/public/codefox.svg create mode 100644 backend/template/react-ts/src/index.jsx create mode 100644 backend/template/react-ts/src/vite-env.d.ts create mode 100644 backend/template/react-ts/tsconfig.app.json create mode 100644 backend/template/react-ts/tsconfig.json create mode 100644 backend/template/react-ts/tsconfig.node.json create mode 100644 backend/template/react-ts/vite.config.ts diff --git a/backend/template/react-ts/.gitignore b/backend/template/react-ts/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/backend/template/react-ts/.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/backend/template/react-ts/README.md b/backend/template/react-ts/README.md new file mode 100644 index 00000000..74872fd4 --- /dev/null +++ b/backend/template/react-ts/README.md @@ -0,0 +1,50 @@ +# 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/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-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: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/backend/template/react-ts/eslint.config.js b/backend/template/react-ts/eslint.config.js new file mode 100644 index 00000000..092408a9 --- /dev/null +++ b/backend/template/react-ts/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/backend/template/react-ts/index.html b/backend/template/react-ts/index.html new file mode 100644 index 00000000..6c13993e --- /dev/null +++ b/backend/template/react-ts/index.html @@ -0,0 +1,13 @@ + + + + + + + Codefox generated project + + +
+ + + diff --git a/backend/template/react-ts/package-lock.json b/backend/template/react-ts/package-lock.json new file mode 100644 index 00000000..99341101 --- /dev/null +++ b/backend/template/react-ts/package-lock.json @@ -0,0 +1,3041 @@ +{ + "name": "react-ts", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "react-ts", + "version": "0.0.0", + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router": "^7.0.1" + }, + "devDependencies": { + "@eslint/js": "^9.15.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.15.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.12.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.15.0", + "vite": "^6.0.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "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, + "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, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", + "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", + "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "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, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", + "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", + "dev": true, + "dependencies": { + "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, + "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, + "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, + "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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "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, + "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, + "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, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz", + "integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz", + "integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz", + "integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz", + "integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz", + "integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz", + "integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz", + "integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz", + "integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz", + "integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz", + "integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz", + "integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz", + "integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz", + "integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz", + "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz", + "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz", + "integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz", + "integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz", + "integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "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 + }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", + "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/type-utils": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.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.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", + "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.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" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", + "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.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/type-utils": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", + "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.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" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", + "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", + "dev": true, + "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.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", + "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.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": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "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, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", + "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.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" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", + "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "eslint-visitor-keys": "^4.2.0" + }, + "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": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "dev": true, + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "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, + "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, + "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, + "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 + }, + "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 + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "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, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "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", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001684", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", + "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "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, + "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, + "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 + }, + "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 + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "engines": { + "node": ">=18" + } + }, + "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, + "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 + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "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 + }, + "node_modules/electron-to-chromium": { + "version": "1.5.66", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.66.tgz", + "integrity": "sha512-pI2QF6+i+zjPbqRzJwkMvtvkdI7MjVbSh2g8dlMguDJIXEPw+kwasS1Jl+YGPEBfGVxsVgGUratAKymPdPo2vQ==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", + "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.15.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.5", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.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.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz", + "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==", + "dev": true, + "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.14", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.14.tgz", + "integrity": "sha512-aXvzCTK7ZBv1e7fahFuR3Z/fyQQSIQ711yPgYRj+Oj64tyTgO4iQIDmYXDBqvSWQ/FA4OSCsXOStlF+noU0/NA==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "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.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "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, + "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, + "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, + "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, + "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 + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "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.4" + }, + "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, + "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 + }, + "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 + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "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, + "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, + "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, + "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, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true + }, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", + "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "dev": true, + "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 + }, + "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, + "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, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "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, + "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, + "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, + "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, + "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 + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "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, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "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 + }, + "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 + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "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, + "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, + "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 + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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, + "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, + "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, + "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 + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "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 + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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, + "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, + "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, + "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, + "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, + "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 + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "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, + "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, + "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" + } + ] + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.1.tgz", + "integrity": "sha512-WVAhv9oWCNsja5AkK6KLpXJDSJCQizOIyOd4vvB/+eHGbYx5vkhcmcmwWjQ9yqkRClogi+xjEg9fNEOd5EX/tw==", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.27.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz", + "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.27.4", + "@rollup/rollup-android-arm64": "4.27.4", + "@rollup/rollup-darwin-arm64": "4.27.4", + "@rollup/rollup-darwin-x64": "4.27.4", + "@rollup/rollup-freebsd-arm64": "4.27.4", + "@rollup/rollup-freebsd-x64": "4.27.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", + "@rollup/rollup-linux-arm-musleabihf": "4.27.4", + "@rollup/rollup-linux-arm64-gnu": "4.27.4", + "@rollup/rollup-linux-arm64-musl": "4.27.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", + "@rollup/rollup-linux-riscv64-gnu": "4.27.4", + "@rollup/rollup-linux-s390x-gnu": "4.27.4", + "@rollup/rollup-linux-x64-gnu": "4.27.4", + "@rollup/rollup-linux-x64-musl": "4.27.4", + "@rollup/rollup-win32-arm64-msvc": "4.27.4", + "@rollup/rollup-win32-ia32-msvc": "4.27.4", + "@rollup/rollup-win32-x64-msvc": "4.27.4", + "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" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" + }, + "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, + "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, + "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, + "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, + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.2.tgz", + "integrity": "sha512-ZF5gQIQa/UmzfvxbHZI3JXN0/Jt+vnAfAviNRAMc491laiK6YCLpCW9ft8oaCRFOTxCZtUTE6XB0ZQAe3olntw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==" + }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.16.0.tgz", + "integrity": "sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.16.0", + "@typescript-eslint/parser": "8.16.0", + "@typescript-eslint/utils": "8.16.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" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.1.tgz", + "integrity": "sha512-Ldn6gorLGr4mCdFnmeAOLweJxZ34HjKnDm4HGo6P66IEqTxQb36VEdFJQENKxWjupNfoIjvRUnswjn1hpYEpjQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.24.0", + "postcss": "^8.4.49", + "rollup": "^4.23.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/backend/template/react-ts/package.json b/backend/template/react-ts/package.json new file mode 100644 index 00000000..a49320d8 --- /dev/null +++ b/backend/template/react-ts/package.json @@ -0,0 +1,30 @@ +{ + "name": "codefox", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router": "^7.0.1" + }, + "devDependencies": { + "@eslint/js": "^9.15.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.15.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.12.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.15.0", + "vite": "^6.0.1" + } +} \ No newline at end of file diff --git a/backend/template/react-ts/public/codefox.svg b/backend/template/react-ts/public/codefox.svg new file mode 100644 index 00000000..f1c44cd9 --- /dev/null +++ b/backend/template/react-ts/public/codefox.svg @@ -0,0 +1,29 @@ + + + +Created with Fabric.js 5.2.4 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/template/react-ts/src/index.jsx b/backend/template/react-ts/src/index.jsx new file mode 100644 index 00000000..ec867e9e --- /dev/null +++ b/backend/template/react-ts/src/index.jsx @@ -0,0 +1,6 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; + +createRoot(document.getElementById('root')).render( + Hello world, +); diff --git a/backend/template/react-ts/src/vite-env.d.ts b/backend/template/react-ts/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/backend/template/react-ts/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/backend/template/react-ts/tsconfig.app.json b/backend/template/react-ts/tsconfig.app.json new file mode 100644 index 00000000..358ca9ba --- /dev/null +++ b/backend/template/react-ts/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/backend/template/react-ts/tsconfig.json b/backend/template/react-ts/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/backend/template/react-ts/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/backend/template/react-ts/tsconfig.node.json b/backend/template/react-ts/tsconfig.node.json new file mode 100644 index 00000000..db0becc8 --- /dev/null +++ b/backend/template/react-ts/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/backend/template/react-ts/vite.config.ts b/backend/template/react-ts/vite.config.ts new file mode 100644 index 00000000..8b0f57b9 --- /dev/null +++ b/backend/template/react-ts/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9cd14fc..463b45ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -101,8 +101,10 @@ importers: version: 0.11.0(graphql@16.9.0) typeorm: specifier: ^0.3.20 - version: 0.3.20(sqlite3@5.1.7)(ts-node@10.9.2(@swc/core@1.9.2(@swc/helpers@0.5.5))(@types/node@20.17.5)(typescript@5.6.3)) + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@eslint/eslintrc': specifier: ^3.1.0 @@ -13555,7 +13557,6 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 - gauge@4.0.4: dependencies: aproba: 2.0.0 @@ -13872,7 +13873,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.2 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.7.4(debug@4.3.7)) + retry-axios: 2.6.0(axios@1.7.4) tough-cookie: 4.1.4 transitivePeerDependencies: - supports-color @@ -16461,7 +16462,7 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - retry-axios@2.6.0(axios@1.7.4(debug@4.3.7)): + retry-axios@2.6.0(axios@1.7.4): dependencies: axios: 1.7.4(debug@4.3.7) From 776a4597d567bcbafbb659d7f001333e1f019d94 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sat, 30 Nov 2024 21:39:31 -0500 Subject: [PATCH 02/40] init file generate --- .../__tests__/test-file-create.spec.ts | 42 +++++++++ .../build-system/node/file-generate/index.ts | 91 +++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 backend/src/build-system/__tests__/test-file-create.spec.ts create mode 100644 backend/src/build-system/node/file-generate/index.ts diff --git a/backend/src/build-system/__tests__/test-file-create.spec.ts b/backend/src/build-system/__tests__/test-file-create.spec.ts new file mode 100644 index 00000000..f819b15b --- /dev/null +++ b/backend/src/build-system/__tests__/test-file-create.spec.ts @@ -0,0 +1,42 @@ +import * as fs from 'fs-extra'; +import * as path from 'path'; +import { FileGeneratorHandler } from '../node/file-generate'; // Update with actual file path to the handler + +describe('FileGeneratorHandler', () => { + const projectSrcPath = 'src\\build-system\\__tests__\\test-project\\src\\'; + + beforeEach(async () => { + // Ensure the project directory is clean + await fs.remove('src\\build-system\\__tests__\\test-project\\src\\'); + }); + + afterEach(async () => { + // Clean up the generated test files + await fs.remove('src\\build-system\\__tests__\\test-project\\src\\'); + }); + + it('should generate files based on file-arch.json', async () => { + const handler = new FileGeneratorHandler(); + + // Read JSON data from file + const jsonFilePath = path.resolve( + 'src\\build-system\\__tests__\\file-arch.json', + ); + const jsonData = fs.readJSONSync(jsonFilePath); + + // Run the file generator with the JSON data + const result = await handler.generateFiles(jsonData, projectSrcPath); + + // Write result to console and verify success + console.log(result); + expect(result.success).toBe(true); + + // Verify files were generated correctly + const generatedFiles = Object.keys(jsonData.files); + for (const fileName of generatedFiles) { + const filePath = path.resolve(projectSrcPath, fileName); + const fileExists = await fs.pathExists(filePath); + expect(fileExists).toBe(true); + } + }, 30000); +}); diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts new file mode 100644 index 00000000..fa3383d8 --- /dev/null +++ b/backend/src/build-system/node/file-generate/index.ts @@ -0,0 +1,91 @@ +import * as fs from 'fs/promises'; +import * as path from 'path'; +import { Logger } from '@nestjs/common'; + +interface FileEntry { + name: string; + dependencies: string[]; +} + +export class FileGeneratorHandler { + private readonly logger = new Logger('FileGeneratorHandler'); + + /** + * Generate files based on JSON input and a specified project source path. + * @param jsonData The JSON file data containing file dependencies. + * @param projectSrcPath The base path where files should be generated. + */ + async generateFiles( + jsonData: { files: Record }, + projectSrcPath: string, + ): Promise<{ success: boolean; data: string }> { + const files = Object.entries(jsonData.files).map(([name, details]) => ({ + name, + dependencies: details.dependsOn, + })); + + if (!files || files.length === 0) { + throw new Error('No files to generate.'); + } + + const generatedFiles = new Set(); + const remainingFiles = [...files]; + + const generatedResult: Record = {}; + + while (remainingFiles.length > 0) { + let generatedInThisStep = false; + + for (const file of remainingFiles) { + if (file.dependencies.every((dep) => generatedFiles.has(dep))) { + const fullPath = path.resolve(projectSrcPath, file.name); + this.logger.log(`Generating file: ${fullPath}`); + await this.createFile(fullPath); + generatedFiles.add(file.name); + generatedResult[file.name] = { dependsOn: file.dependencies }; + remainingFiles.splice(remainingFiles.indexOf(file), 1); + generatedInThisStep = true; + } + } + + if (!generatedInThisStep) { + const unresolved = remainingFiles.map((file) => ({ + file: file.name, + missingDependencies: file.dependencies.filter( + (dep) => !generatedFiles.has(dep), + ), + })); + this.logger.error( + 'Unresolved dependencies:', + JSON.stringify(unresolved, null, 2), + ); + throw new Error('Circular or unresolved dependencies detected.'); + } + } + + this.logger.log('All files generated successfully.'); + + // Return the result as a formatted string + return { + success: true, + data: `${JSON.stringify({ files: generatedResult }, null, 2)}`, + }; + } + + /** + * Create a file, including creating necessary directories. + * @param filePath The full path of the file to create. + */ + private async createFile(filePath: string): Promise { + const dir = path.dirname(filePath); + + // Ensure the directory exists + await fs.mkdir(dir, { recursive: true }); + + // Create the file with a placeholder content + const content = `// Generated file: ${path.basename(filePath)}`; + await fs.writeFile(filePath, content, 'utf8'); + + this.logger.log(`File created: ${filePath}`); + } +} From 498e54a0edd4dce398e0b8bbd05bc3368512edbc Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sat, 30 Nov 2024 23:27:00 -0500 Subject: [PATCH 03/40] add one step --- .../__tests__/test-generate-doc.spec.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/backend/src/build-system/__tests__/test-generate-doc.spec.ts b/backend/src/build-system/__tests__/test-generate-doc.spec.ts index 972b93bb..dba9d8e3 100644 --- a/backend/src/build-system/__tests__/test-generate-doc.spec.ts +++ b/backend/src/build-system/__tests__/test-generate-doc.spec.ts @@ -75,6 +75,26 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { }, ], }, + { + id: 'step-5', + name: 'UX Data Map Document', + nodes: [ + { + id: 'op:UX_DATAMAP::STATE:GENERATE', + name: 'UX Data Map Document node', + }, + ], + }, + // { + // id: 'step-6', + // name: 'File_Arch Document', + // nodes: [ + // { + // id: 'op:FILE_ARCH::STATE:GENERATE', + // name: 'File_Arch', + // }, + // ], + // }, ], }; @@ -91,6 +111,7 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { sequence.steps.forEach((step) => { step.nodes.forEach((node) => { const resultData = context.getResult(node.id); + console.log(resultData); if (resultData) { writeMarkdownToFile(node.name.replace(/ /g, '_'), resultData); } From a5788d45ec8b83fd6b24fcbfe7c219c7cb963856 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 09:24:45 -0500 Subject: [PATCH 04/40] add file arch to manager --- backend/src/build-system/hanlder-manager.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/build-system/hanlder-manager.ts b/backend/src/build-system/hanlder-manager.ts index 3383a318..4760a77d 100644 --- a/backend/src/build-system/hanlder-manager.ts +++ b/backend/src/build-system/hanlder-manager.ts @@ -5,6 +5,7 @@ import { UXSitemapStructureHandler } from './node/ux-sitemap-structure'; import { UXDatamapHandler } from './node/ux-datamap'; import { UXSMDHandler } from './node/ux-sitemap-document/uxsmd'; import { FileStructureHandler } from './node/frontend-file-structure'; +import { FileArchGenerateHandler } from './node/file-arch'; export class BuildHandlerManager { private static instance: BuildHandlerManager; @@ -22,6 +23,7 @@ export class BuildHandlerManager { new UXDatamapHandler(), new UXSMDHandler(), new FileStructureHandler(), + new FileArchGenerateHandler(), ]; for (const handler of builtInHandlers) { From f9f59366feacc832f961563e9e731ade47abd04e Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 11:39:38 -0500 Subject: [PATCH 05/40] add file arch --- .../__tests__/test-generate-doc.spec.ts | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/backend/src/build-system/__tests__/test-generate-doc.spec.ts b/backend/src/build-system/__tests__/test-generate-doc.spec.ts index dba9d8e3..3cdad1ff 100644 --- a/backend/src/build-system/__tests__/test-generate-doc.spec.ts +++ b/backend/src/build-system/__tests__/test-generate-doc.spec.ts @@ -67,34 +67,43 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { }, { id: 'step-4', + name: 'UX Data Map Document', + nodes: [ + { + id: 'op:UX_DATAMAP::STATE:GENERATE', + name: 'UX Data Map Document node', + requires: ['op:UXSMD::STATE:GENERATE'], + }, + ], + }, + { + id: 'step-5', name: 'file structure generation', nodes: [ { id: 'op:FSTRUCT::STATE:GENERATE', name: 'file structure generation', + requires: [ + 'op:UXSMD::STATE:GENERATE', + 'op:UX_DATAMAP::STATE:GENERATE', + ], }, ], }, { - id: 'step-5', - name: 'UX Data Map Document', + id: 'step-6', + name: 'File_Arch Document', nodes: [ { - id: 'op:UX_DATAMAP::STATE:GENERATE', - name: 'UX Data Map Document node', + id: 'op:FILE_ARCH::STATE:GENERATE', + name: 'File_Arch', + requires: [ + 'op:FSTRUCT::STATE:GENERATE', + 'op:UX_DATAMAP::STATE:GENERATE', + ], }, ], }, - // { - // id: 'step-6', - // name: 'File_Arch Document', - // nodes: [ - // { - // id: 'op:FILE_ARCH::STATE:GENERATE', - // name: 'File_Arch', - // }, - // ], - // }, ], }; @@ -108,15 +117,15 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { try { await BuildSequenceExecutor.executeSequence(sequence, context); - sequence.steps.forEach((step) => { - step.nodes.forEach((node) => { - const resultData = context.getResult(node.id); + for (const step of sequence.steps) { + for (const node of step.nodes) { + const resultData = await context.getResult(node.id); console.log(resultData); if (resultData) { writeMarkdownToFile(node.name.replace(/ /g, '_'), resultData); } - }); - }); + } + } console.log( 'Sequence completed successfully. Logs stored in:', @@ -131,5 +140,5 @@ describe('Sequence: PRD -> UXSD -> UXDD -> UXSS', () => { ); throw error; } - }, 60000); + }, 600000); }); From 0aabf71a1b02cdffca24916ed44d327ca3ddc578 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 11:43:06 -0500 Subject: [PATCH 06/40] fix passing input to prompt --- .../src/build-system/node/file-arch/index.ts | 12 +++++++++--- .../node/frontend-file-structure/index.ts | 17 ++++++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/backend/src/build-system/node/file-arch/index.ts b/backend/src/build-system/node/file-arch/index.ts index dd1321d8..25cd1c9c 100644 --- a/backend/src/build-system/node/file-arch/index.ts +++ b/backend/src/build-system/node/file-arch/index.ts @@ -4,11 +4,11 @@ import { generateFileArchPrompt } from './prompt'; import { Logger } from '@nestjs/common'; export class FileArchGenerateHandler implements BuildHandler { - readonly id = 'op:File_Arch::STATE:GENERATE'; + readonly id = 'op:FILE_ARCH::STATE:GENERATE'; private readonly logger: Logger = new Logger('FileArchGenerateHandler'); // TODO: adding page by page analysis - async run(context: BuilderContext, ...args: any[]): Promise { + async run(context: BuilderContext, args: unknown): Promise { this.logger.log('Generating File Architecture Document...'); const fileStructure = args[0] as string; @@ -23,7 +23,13 @@ export class FileArchGenerateHandler implements BuildHandler { }; } - const prompt = generateFileArchPrompt(fileStructure, dataMapStruct); + const prompt = generateFileArchPrompt( + JSON.stringify(fileStructure, null, 2), + JSON.stringify(dataMapStruct, null, 2), + ); + this.logger.log( + 'generateFileArchPrompt: ' + JSON.stringify(prompt, null, 2), + ); const fileArchContent = await context.model.chatSync( { diff --git a/backend/src/build-system/node/frontend-file-structure/index.ts b/backend/src/build-system/node/frontend-file-structure/index.ts index 380e9112..bd277929 100644 --- a/backend/src/build-system/node/frontend-file-structure/index.ts +++ b/backend/src/build-system/node/frontend-file-structure/index.ts @@ -2,9 +2,11 @@ import { BuildHandler, BuildResult } from 'src/build-system/types'; import { BuilderContext } from 'src/build-system/context'; import { ModelProvider } from 'src/common/model-provider'; import { prompts } from './prompt'; +import { Logger } from '@nestjs/common'; export class FileStructureHandler implements BuildHandler { readonly id = 'op:FSTRUCT::STATE:GENERATE'; + private readonly logger: Logger = new Logger('FileStructureHandler'); async run(context: BuilderContext, args: unknown): Promise { console.log('Generating File Structure Document...'); @@ -13,11 +15,20 @@ export class FileStructureHandler implements BuildHandler { const projectName = context.getData('projectName') || 'Default Project Name'; + const sitemapDoc = args[0] as string; + const dataMap = args[1] as string; + + if (!dataMap || !sitemapDoc) { + return { + success: false, + error: new Error('Missing required parameters: sitemapDoc or dataMap'), + }; + } + const prompt = prompts.generateFileStructurePrompt( projectName, - args as string, - // TODO: change later - args as string, + JSON.stringify(sitemapDoc, null, 2), + JSON.stringify(dataMap, null, 2), 'FrameWork Holder', ); From 361312be88323f0bc3a91e169a5d5c61c2093a90 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 14:06:32 -0500 Subject: [PATCH 07/40] fix passing problem --- .../build-system/node/ux-sitemap-structure/index.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/src/build-system/node/ux-sitemap-structure/index.ts b/backend/src/build-system/node/ux-sitemap-structure/index.ts index 60ffdcb4..8ce2bfaf 100644 --- a/backend/src/build-system/node/ux-sitemap-structure/index.ts +++ b/backend/src/build-system/node/ux-sitemap-structure/index.ts @@ -16,13 +16,22 @@ export class UXSitemapStructureHandler implements BuildHandler { const projectName = context.getData('projectName') || 'Default Project Name'; + const sitemap = args[0] as string; + + if (!sitemap) { + return { + success: false, + error: new Error('Missing required parameters: sitemap'), + }; + } + const prompt = prompts.generateUXSiteMapStructrePrompt( projectName, - args as string, + JSON.stringify(sitemap, null, 2), // TODO: change later 'web', ); - + this.logger.log(prompt); const uxStructureContent = await context.model.chatSync( { content: prompt, From 12a02e2001b3be141fa2adfc1ac31a638d088b95 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 14:12:37 -0500 Subject: [PATCH 08/40] file create read markdown and test case --- .../__tests__/test-file-create.spec.ts | 38 ++++++---- .../build-system/node/file-generate/index.ts | 73 +++++++++++++++++-- 2 files changed, 92 insertions(+), 19 deletions(-) diff --git a/backend/src/build-system/__tests__/test-file-create.spec.ts b/backend/src/build-system/__tests__/test-file-create.spec.ts index f819b15b..3e40f68f 100644 --- a/backend/src/build-system/__tests__/test-file-create.spec.ts +++ b/backend/src/build-system/__tests__/test-file-create.spec.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { FileGeneratorHandler } from '../node/file-generate'; // Update with actual file path to the handler describe('FileGeneratorHandler', () => { - const projectSrcPath = 'src\\build-system\\__tests__\\test-project\\src\\'; + const projectSrcPath = 'src\\build-system\\__tests__\\test-project\\'; beforeEach(async () => { // Ensure the project directory is clean @@ -19,24 +19,34 @@ describe('FileGeneratorHandler', () => { const handler = new FileGeneratorHandler(); // Read JSON data from file - const jsonFilePath = path.resolve( - 'src\\build-system\\__tests__\\file-arch.json', + const mdFilePath = path.resolve( + 'src\\build-system\\__tests__\\file-arch.md', ); - const jsonData = fs.readJSONSync(jsonFilePath); - // Run the file generator with the JSON data - const result = await handler.generateFiles(jsonData, projectSrcPath); + const markdownContent = fs.readFileSync(path.resolve(mdFilePath), 'utf8'); - // Write result to console and verify success - console.log(result); - expect(result.success).toBe(true); + // Run the file generator with the JSON data + const result = await handler.generateFiles(markdownContent, projectSrcPath); // Verify files were generated correctly - const generatedFiles = Object.keys(jsonData.files); - for (const fileName of generatedFiles) { - const filePath = path.resolve(projectSrcPath, fileName); - const fileExists = await fs.pathExists(filePath); - expect(fileExists).toBe(true); + // const generatedFiles = Object.keys(jsonData.files); + // for (const fileName of generatedFiles) { + // const filePath = path.resolve(projectSrcPath, fileName); + // const fileExists = await fs.pathExists(filePath); + // expect(fileExists).toBe(true); + // } + + console.log('File generation result:', result); + + // Verify that all files exist + const jsonData = JSON.parse( + /([\s\S]*?)<\/GENERATEDCODE>/.exec(markdownContent)![1], + ); + const files = Object.keys(jsonData.files); + + for (const file of files) { + const filePath = path.resolve(projectSrcPath, file); + expect(fs.existsSync(filePath)).toBeTruthy(); } }, 30000); }); diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index fa3383d8..b671fb07 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -16,12 +16,40 @@ export class FileGeneratorHandler { * @param projectSrcPath The base path where files should be generated. */ async generateFiles( - jsonData: { files: Record }, + markdownContent: string, projectSrcPath: string, ): Promise<{ success: boolean; data: string }> { + const resolveDependencyPath = ( + filePath: string, + dependency: string, + projectSrcPath: string, + ): string => { + const fileDir = path.dirname(filePath); // Directory of the current file + let resolvedPath = path.resolve(fileDir, dependency); // Resolve relative to the file's location + + // If the dependency doesn't have an extension, assume it's a folder with an index file + if (!path.extname(resolvedPath)) { + const isCSS = dependency.includes('css'); + resolvedPath = isCSS + ? `${resolvedPath}/index.css` + : `${resolvedPath}/index.ts`; + } + + // Ensure the resolved path is within the project source path + return path.relative(projectSrcPath, resolvedPath); + }; + + const jsonData = this.extractJsonFromMarkdown(markdownContent); + const files = Object.entries(jsonData.files).map(([name, details]) => ({ name, - dependencies: details.dependsOn, + dependencies: details.dependsOn.map((dep) => + resolveDependencyPath( + path.resolve(projectSrcPath, name), + dep, + projectSrcPath, + ), + ), })); if (!files || files.length === 0) { @@ -30,14 +58,25 @@ export class FileGeneratorHandler { const generatedFiles = new Set(); const remainingFiles = [...files]; - const generatedResult: Record = {}; + const visited = new Set(); while (remainingFiles.length > 0) { let generatedInThisStep = false; for (const file of remainingFiles) { - if (file.dependencies.every((dep) => generatedFiles.has(dep))) { + if (visited.has(file.name)) { + this.logger.error(`Circular dependency detected: ${file.name}`); + continue; + } + + visited.add(file.name); + + const unresolvedDeps = file.dependencies.filter( + (dep) => !generatedFiles.has(dep), + ); + + if (unresolvedDeps.length === 0) { const fullPath = path.resolve(projectSrcPath, file.name); this.logger.log(`Generating file: ${fullPath}`); await this.createFile(fullPath); @@ -45,6 +84,10 @@ export class FileGeneratorHandler { generatedResult[file.name] = { dependsOn: file.dependencies }; remainingFiles.splice(remainingFiles.indexOf(file), 1); generatedInThisStep = true; + } else { + this.logger.warn( + `Unresolved dependencies for ${file.name}: ${JSON.stringify(unresolvedDeps)}`, + ); } } @@ -65,13 +108,33 @@ export class FileGeneratorHandler { this.logger.log('All files generated successfully.'); - // Return the result as a formatted string return { success: true, data: `${JSON.stringify({ files: generatedResult }, null, 2)}`, }; } + /** + * Extract JSON data from Markdown content. + * @param markdownContent The Markdown content containing the JSON. + */ + private extractJsonFromMarkdown(markdownContent: string): { + files: Record; + } { + const jsonMatch = /([\s\S]*?)<\/GENERATEDCODE>/m.exec( + markdownContent, + ); + if (!jsonMatch) { + throw new Error('No JSON found in the provided Markdown content.'); + } + + try { + return JSON.parse(jsonMatch[1]); + } catch (error) { + throw new Error('Invalid JSON format in the Markdown content.'); + } + } + /** * Create a file, including creating necessary directories. * @param filePath The full path of the file to create. From b9a69d9a94b84b90ca7b0f2a31fab7646b3f8124 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 14:12:47 -0500 Subject: [PATCH 09/40] test file --- .../src/build-system/__tests__/file-arch.md | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 backend/src/build-system/__tests__/file-arch.md diff --git a/backend/src/build-system/__tests__/file-arch.md b/backend/src/build-system/__tests__/file-arch.md new file mode 100644 index 00000000..a6b338b5 --- /dev/null +++ b/backend/src/build-system/__tests__/file-arch.md @@ -0,0 +1,129 @@ + +{ + "files": { + "src/api/auth.ts": { + "dependsOn": [] + }, + "src/api/music.ts": { + "dependsOn": [] + }, + "src/api/user.ts": { + "dependsOn": [] + }, + "src/api/index.ts": { + "dependsOn": ["./auth", "./music", "./user"] + }, + "src/components/common/Button/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/common/Input/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/common/Loader/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/layout/Header/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/layout/Footer/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/layout/Sidebar/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/specific/MusicDetail/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/specific/Playlist/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/specific/Search/index.tsx": { + "dependsOn": ["./index.css"] + }, + "src/components/index.ts": { + "dependsOn": ["./common/Button", "./common/Input", "./common/Loader", "./layout/Header", "./layout/Footer", "./layout/Sidebar", "./specific/MusicDetail", "./specific/Playlist", "./specific/Search"] + }, + "src/contexts/AuthContext.tsx": { + "dependsOn": [] + }, + "src/contexts/ThemeContext.tsx": { + "dependsOn": [] + }, + "src/contexts/PlayerContext.tsx": { + "dependsOn": [] + }, + "src/contexts/index.ts": { + "dependsOn": ["./AuthContext", "./ThemeContext", "./PlayerContext"] + }, + "src/hooks/useAuth.ts": { + "dependsOn": ["../contexts/AuthContext"] + }, + "src/hooks/useMusic.ts": { + "dependsOn": [] + }, + "src/hooks/useUser.ts": { + "dependsOn": ["../contexts/AuthContext"] + }, + "src/hooks/index.ts": { + "dependsOn": ["./useAuth", "./useMusic", "./useUser"] + }, + "src/pages/Home/index.tsx": { + "dependsOn": ["./Home.css", "../../components", "../../hooks/useMusic", "../../hooks/useUser"] + }, + "src/pages/Home/Home.css": { + "dependsOn": [] + }, + "src/pages/Discover/index.tsx": { + "dependsOn": ["./Discover.css", "../../components", "../../hooks/useMusic"] + }, + "src/pages/Discover/Discover.css": { + "dependsOn": [] + }, + "src/pages/Playlists/index.tsx": { + "dependsOn": ["./Playlists.css", "../../components", "../../hooks/useMusic", "../../hooks/useUser"] + }, + "src/pages/Playlists/Playlists.css": { + "dependsOn": [] + }, + "src/pages/Artists/index.tsx": { + "dependsOn": ["./Artists.css", "../../components"] + }, + "src/pages/Artists/Artists.css": { + "dependsOn": [] + }, + "src/pages/Profile/index.tsx": { + "dependsOn": ["./Profile.css", "../../components", "../../hooks/useUser"] + }, + "src/pages/Profile/Profile.css": { + "dependsOn": [] + }, + "src/pages/Login/index.tsx": { + "dependsOn": ["./Login.css", "../../components", "../../hooks/useAuth"] + }, + "src/pages/Login/Login.css": { + "dependsOn": [] + }, + "src/pages/index.ts": { + "dependsOn": ["./Home", "./Discover", "./Playlists", "./Artists", "./Profile", "./Login"] + }, + "src/router.ts": { + "dependsOn": ["./pages"] + }, + "src/index.ts": { + "dependsOn": ["./router"] + }, + "src/utils/constants.ts": { + "dependsOn": [] + }, + "src/utils/helpers.ts": { + "dependsOn": [] + }, + "src/utils/validators.ts": { + "dependsOn": [] + }, + "src/utils/index.ts": { + "dependsOn": ["./constants", "./helpers", "./validators"] + } + } +} + \ No newline at end of file From 91b550cfefd58f1a54ff7ab189ebe070794d6716 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 15:23:51 -0500 Subject: [PATCH 10/40] first generate no dependency --- .../build-system/node/file-generate/index.ts | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index b671fb07..6eee5609 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -56,33 +56,39 @@ export class FileGeneratorHandler { throw new Error('No files to generate.'); } + const independentFiles = files.filter( + (file) => file.dependencies.length === 0, + ); + const dependentFiles = files.filter((file) => file.dependencies.length > 0); + const generatedFiles = new Set(); - const remainingFiles = [...files]; const generatedResult: Record = {}; - const visited = new Set(); - - while (remainingFiles.length > 0) { - let generatedInThisStep = false; - for (const file of remainingFiles) { - if (visited.has(file.name)) { - this.logger.error(`Circular dependency detected: ${file.name}`); - continue; - } + // **Step 1: Generate all independent files** + for (const file of independentFiles) { + const fullPath = path.resolve(projectSrcPath, file.name); + this.logger.log(`Generating independent file: ${fullPath}`); + await this.createFile(fullPath); + generatedFiles.add(file.name); + generatedResult[file.name] = { dependsOn: file.dependencies }; + } - visited.add(file.name); + // **Step 2: Iteratively resolve and generate dependent files** + while (dependentFiles.length > 0) { + let generatedInThisStep = false; + for (const file of dependentFiles) { const unresolvedDeps = file.dependencies.filter( (dep) => !generatedFiles.has(dep), ); if (unresolvedDeps.length === 0) { const fullPath = path.resolve(projectSrcPath, file.name); - this.logger.log(`Generating file: ${fullPath}`); + this.logger.log(`Generating dependent file: ${fullPath}`); await this.createFile(fullPath); generatedFiles.add(file.name); generatedResult[file.name] = { dependsOn: file.dependencies }; - remainingFiles.splice(remainingFiles.indexOf(file), 1); + dependentFiles.splice(dependentFiles.indexOf(file), 1); generatedInThisStep = true; } else { this.logger.warn( @@ -92,7 +98,7 @@ export class FileGeneratorHandler { } if (!generatedInThisStep) { - const unresolved = remainingFiles.map((file) => ({ + const unresolved = dependentFiles.map((file) => ({ file: file.name, missingDependencies: file.dependencies.filter( (dep) => !generatedFiles.has(dep), From 8d7d848459a0959ad0162b50b6695c3b3b7e9be5 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 19:18:10 -0500 Subject: [PATCH 11/40] correct handle path --- .../build-system/node/file-generate/index.ts | 76 ++++++++----------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index 6eee5609..73b70ea5 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -2,78 +2,43 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { Logger } from '@nestjs/common'; -interface FileEntry { - name: string; - dependencies: string[]; -} - export class FileGeneratorHandler { private readonly logger = new Logger('FileGeneratorHandler'); /** - * Generate files based on JSON input and a specified project source path. - * @param jsonData The JSON file data containing file dependencies. - * @param projectSrcPath The base path where files should be generated. + * Generate files based on the JSON extracted from a Markdown file. + * @param markdownContent The Markdown content containing the JSON. + * @param projectSrcPath The base directory where files should be generated. */ async generateFiles( markdownContent: string, projectSrcPath: string, ): Promise<{ success: boolean; data: string }> { - const resolveDependencyPath = ( - filePath: string, - dependency: string, - projectSrcPath: string, - ): string => { - const fileDir = path.dirname(filePath); // Directory of the current file - let resolvedPath = path.resolve(fileDir, dependency); // Resolve relative to the file's location - - // If the dependency doesn't have an extension, assume it's a folder with an index file - if (!path.extname(resolvedPath)) { - const isCSS = dependency.includes('css'); - resolvedPath = isCSS - ? `${resolvedPath}/index.css` - : `${resolvedPath}/index.ts`; - } - - // Ensure the resolved path is within the project source path - return path.relative(projectSrcPath, resolvedPath); - }; - const jsonData = this.extractJsonFromMarkdown(markdownContent); const files = Object.entries(jsonData.files).map(([name, details]) => ({ name, dependencies: details.dependsOn.map((dep) => - resolveDependencyPath( - path.resolve(projectSrcPath, name), - dep, - projectSrcPath, - ), + this.resolveDependency(name, dep), ), })); - if (!files || files.length === 0) { - throw new Error('No files to generate.'); - } - const independentFiles = files.filter( (file) => file.dependencies.length === 0, ); const dependentFiles = files.filter((file) => file.dependencies.length > 0); const generatedFiles = new Set(); - const generatedResult: Record = {}; - // **Step 1: Generate all independent files** + // Step 1: Generate all independent files for (const file of independentFiles) { const fullPath = path.resolve(projectSrcPath, file.name); this.logger.log(`Generating independent file: ${fullPath}`); await this.createFile(fullPath); generatedFiles.add(file.name); - generatedResult[file.name] = { dependsOn: file.dependencies }; } - // **Step 2: Iteratively resolve and generate dependent files** + // Step 2: Generate dependent files, resolving dependencies iteratively while (dependentFiles.length > 0) { let generatedInThisStep = false; @@ -87,12 +52,13 @@ export class FileGeneratorHandler { this.logger.log(`Generating dependent file: ${fullPath}`); await this.createFile(fullPath); generatedFiles.add(file.name); - generatedResult[file.name] = { dependsOn: file.dependencies }; dependentFiles.splice(dependentFiles.indexOf(file), 1); generatedInThisStep = true; } else { this.logger.warn( - `Unresolved dependencies for ${file.name}: ${JSON.stringify(unresolvedDeps)}`, + `Unresolved dependencies for ${file.name}: ${JSON.stringify( + unresolvedDeps, + )}`, ); } } @@ -116,10 +82,32 @@ export class FileGeneratorHandler { return { success: true, - data: `${JSON.stringify({ files: generatedResult }, null, 2)}`, + data: 'Files and dependencies created successfully.', }; } + /** + * Resolve a dependency path relative to the current file. + * @param currentFile The current file's path. + * @param dependency The dependency path. + */ + private resolveDependency(currentFile: string, dependency: string): string { + const currentDir = path.dirname(currentFile); + + // Check if the dependency is a file with an extension + const hasExtension = path.extname(dependency).length > 0; + + // If the dependency doesn't have an extension and is not CSS/JS, assume it's a TypeScript file + if (!hasExtension) { + dependency = path.join(dependency, 'index.ts'); + } + + // Resolve the dependency path relative to the current directory + const resolvedPath = path.join(currentDir, dependency).replace(/\\/g, '/'); + this.logger.log(`Resolved dependency: ${resolvedPath}`); + return resolvedPath; + } + /** * Extract JSON data from Markdown content. * @param markdownContent The Markdown content containing the JSON. From 842ba94708afdc1ce0d911765c63fc593240db15 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 21:57:09 -0500 Subject: [PATCH 12/40] add valid path --- .../build-system/node/file-generate/index.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index 73b70ea5..cdd4c69e 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -16,6 +16,8 @@ export class FileGeneratorHandler { ): Promise<{ success: boolean; data: string }> { const jsonData = this.extractJsonFromMarkdown(markdownContent); + this.validateJsonData(jsonData); + const files = Object.entries(jsonData.files).map(([name, details]) => ({ name, dependencies: details.dependsOn.map((dep) => @@ -86,6 +88,40 @@ export class FileGeneratorHandler { }; } + /** + * Validate the structure and content of the JSON data. + * @param jsonData The JSON data to validate. + * @throws Error if validation fails. + */ + private validateJsonData(jsonData: { + files: Record; + }): void { + const validPathRegex = /^[a-zA-Z0-9_\-/.]+$/; + + for (const [file, details] of Object.entries(jsonData.files)) { + // Validate the file path + if (!validPathRegex.test(file)) { + throw new Error(`Invalid file path: ${file}`); + } + + // Validate dependencies + for (const dependency of details.dependsOn) { + if (!validPathRegex.test(dependency)) { + throw new Error( + `Invalid dependency path "${dependency}" in file "${file}".`, + ); + } + + // Ensure no double slashes or trailing slashes + if (dependency.includes('//') || dependency.endsWith('/')) { + throw new Error( + `Malformed dependency path "${dependency}" in file "${file}".`, + ); + } + } + } + } + /** * Resolve a dependency path relative to the current file. * @param currentFile The current file's path. From 241091033d77ac9eba184f8d3f7219c26e820731 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 1 Dec 2024 22:21:51 -0500 Subject: [PATCH 13/40] add circuit dependency check --- .../build-system/node/file-generate/index.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index cdd4c69e..eb3d4a62 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -17,6 +17,7 @@ export class FileGeneratorHandler { const jsonData = this.extractJsonFromMarkdown(markdownContent); this.validateJsonData(jsonData); + this.detectCycles(jsonData); const files = Object.entries(jsonData.files).map(([name, details]) => ({ name, @@ -88,6 +89,44 @@ export class FileGeneratorHandler { }; } + /** + * Detect circular dependencies in the JSON data. + * @param jsonData The JSON data to analyze. + * @throws Error if a circular dependency is detected. + */ + private detectCycles(jsonData: { + files: Record; + }): void { + const graph = jsonData.files; + const visited = new Set(); + const currentPath = new Set(); + + const dfs = (node: string): void => { + if (currentPath.has(node)) { + throw new Error( + `Circular dependency detected: ${[...currentPath, node].join(' -> ')}`, + ); + } + + if (!visited.has(node)) { + currentPath.add(node); + visited.add(node); + + for (const dependency of graph[node]?.dependsOn || []) { + dfs(dependency); + } + + currentPath.delete(node); + } + }; + + for (const file of Object.keys(graph)) { + if (!visited.has(file)) { + dfs(file); + } + } + } + /** * Validate the structure and content of the JSON data. * @param jsonData The JSON data to validate. From f6276851d3eb754176cc4528531b7dcc83393862 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 03:22:34 +0000 Subject: [PATCH 14/40] [autofix.ci] apply automated fixes --- backend/src/build-system/__tests__/file-arch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/build-system/__tests__/file-arch.md b/backend/src/build-system/__tests__/file-arch.md index a6b338b5..cc33d3e4 100644 --- a/backend/src/build-system/__tests__/file-arch.md +++ b/backend/src/build-system/__tests__/file-arch.md @@ -126,4 +126,4 @@ } } } - \ No newline at end of file + From 505deef2e6697acd1dceb4edc803ff1b489803bc Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Wed, 4 Dec 2024 20:22:30 -0500 Subject: [PATCH 15/40] use toposort --- .../build-system/node/file-generate/index.ts | 145 +++++++----------- 1 file changed, 59 insertions(+), 86 deletions(-) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index eb3d4a62..c9b8bc79 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -1,6 +1,7 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { Logger } from '@nestjs/common'; +import * as toposort from 'toposort'; export class FileGeneratorHandler { private readonly logger = new Logger('FileGeneratorHandler'); @@ -15,74 +16,23 @@ export class FileGeneratorHandler { projectSrcPath: string, ): Promise<{ success: boolean; data: string }> { const jsonData = this.extractJsonFromMarkdown(markdownContent); - this.validateJsonData(jsonData); - this.detectCycles(jsonData); - - const files = Object.entries(jsonData.files).map(([name, details]) => ({ - name, - dependencies: details.dependsOn.map((dep) => - this.resolveDependency(name, dep), - ), - })); - const independentFiles = files.filter( - (file) => file.dependencies.length === 0, - ); - const dependentFiles = files.filter((file) => file.dependencies.length > 0); + // Build the dependency graph and detect cycles before any file operations + const { graph, nodes } = this.buildDependencyGraph(jsonData); + this.detectCycles(graph); - const generatedFiles = new Set(); + // After validation and cycle detection, perform topological sort + const sortedFiles = this.getSortedFiles(graph, nodes); - // Step 1: Generate all independent files - for (const file of independentFiles) { - const fullPath = path.resolve(projectSrcPath, file.name); - this.logger.log(`Generating independent file: ${fullPath}`); + // Generate files in the correct order + for (const file of sortedFiles) { + const fullPath = path.resolve(projectSrcPath, file); + this.logger.log(`Generating file in dependency order: ${fullPath}`); await this.createFile(fullPath); - generatedFiles.add(file.name); - } - - // Step 2: Generate dependent files, resolving dependencies iteratively - while (dependentFiles.length > 0) { - let generatedInThisStep = false; - - for (const file of dependentFiles) { - const unresolvedDeps = file.dependencies.filter( - (dep) => !generatedFiles.has(dep), - ); - - if (unresolvedDeps.length === 0) { - const fullPath = path.resolve(projectSrcPath, file.name); - this.logger.log(`Generating dependent file: ${fullPath}`); - await this.createFile(fullPath); - generatedFiles.add(file.name); - dependentFiles.splice(dependentFiles.indexOf(file), 1); - generatedInThisStep = true; - } else { - this.logger.warn( - `Unresolved dependencies for ${file.name}: ${JSON.stringify( - unresolvedDeps, - )}`, - ); - } - } - - if (!generatedInThisStep) { - const unresolved = dependentFiles.map((file) => ({ - file: file.name, - missingDependencies: file.dependencies.filter( - (dep) => !generatedFiles.has(dep), - ), - })); - this.logger.error( - 'Unresolved dependencies:', - JSON.stringify(unresolved, null, 2), - ); - throw new Error('Circular or unresolved dependencies detected.'); - } } this.logger.log('All files generated successfully.'); - return { success: true, data: 'Files and dependencies created successfully.', @@ -90,41 +40,64 @@ export class FileGeneratorHandler { } /** - * Detect circular dependencies in the JSON data. - * @param jsonData The JSON data to analyze. - * @throws Error if a circular dependency is detected. + * Build dependency graph from JSON data. + * @param jsonData The JSON data containing file dependencies. */ - private detectCycles(jsonData: { + private buildDependencyGraph(jsonData: { files: Record; - }): void { - const graph = jsonData.files; - const visited = new Set(); - const currentPath = new Set(); + }): { graph: [string, string][]; nodes: Set } { + const graph: [string, string][] = []; + const nodes = new Set(); + + Object.entries(jsonData.files).forEach(([fileName, details]) => { + nodes.add(fileName); + details.dependsOn.forEach((dep) => { + const resolvedDep = this.resolveDependency(fileName, dep); + graph.push([resolvedDep, fileName]); // [dependency, dependent] + nodes.add(resolvedDep); + }); + }); + + return { graph, nodes }; + } - const dfs = (node: string): void => { - if (currentPath.has(node)) { + /** + * Detect cycles in the dependency graph before any file operations. + * @param graph The dependency graph to check. + * @throws Error if a cycle is detected. + */ + private detectCycles(graph: [string, string][]): void { + try { + toposort(graph); + } catch (error) { + if (error.message.includes('cycle')) { throw new Error( - `Circular dependency detected: ${[...currentPath, node].join(' -> ')}`, + `Circular dependency detected in the file structure: ${error.message}`, ); } + throw error; + } + } - if (!visited.has(node)) { - currentPath.add(node); - visited.add(node); - - for (const dependency of graph[node]?.dependsOn || []) { - dfs(dependency); - } - - currentPath.delete(node); + /** + * Get topologically sorted list of files. + * @param graph The dependency graph. + * @param nodes Set of all nodes. + */ + private getSortedFiles( + graph: [string, string][], + nodes: Set, + ): string[] { + const sortedFiles = toposort(graph).reverse(); + + // Add any files that have no dependencies and weren't included in the sort + Array.from(nodes).forEach((node) => { + if (!sortedFiles.includes(node)) { + sortedFiles.unshift(node); } - }; + }); - for (const file of Object.keys(graph)) { - if (!visited.has(file)) { - dfs(file); - } - } + return sortedFiles; } /** From 21fe5e5b4480040a3a29ee7d882ef544af30a85c Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Fri, 6 Dec 2024 14:08:50 -0500 Subject: [PATCH 16/40] virtual directory --- .../__tests__/testVirtualDir.spec.ts | 58 +++++++ .../node/file-generate/Virtual-Directory.ts | 152 ++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 backend/src/build-system/__tests__/testVirtualDir.spec.ts create mode 100644 backend/src/build-system/node/file-generate/Virtual-Directory.ts diff --git a/backend/src/build-system/__tests__/testVirtualDir.spec.ts b/backend/src/build-system/__tests__/testVirtualDir.spec.ts new file mode 100644 index 00000000..66f74c84 --- /dev/null +++ b/backend/src/build-system/__tests__/testVirtualDir.spec.ts @@ -0,0 +1,58 @@ +import * as fs from 'fs-extra'; +import * as path from 'path'; +import { FileGeneratorHandler } from '../node/file-generate'; +import { VirtualDirectory } from '../node/file-generate/Virtual-Directory'; + +describe('FileGeneratorHandler and VirtualDirectory', () => { + const structMdFilePath = path.resolve( + 'src\\build-system\\__tests__\\file-structure-document.md', + ); + + beforeEach(async () => { + await fs.remove('src\\build-system\\__tests__\\test-project\\src\\'); + }); + + afterEach(async () => { + await fs.remove('src\\build-system\\__tests__\\test-project\\src\\'); + }); + + describe('VirtualDirectory', () => { + let virtualDir: VirtualDirectory; + let structMarkdownContent: string; + + beforeEach(() => { + structMarkdownContent = fs.readFileSync(structMdFilePath, 'utf8'); + virtualDir = new VirtualDirectory(structMarkdownContent); + }); + + it('should print tree structure', () => { + const files = virtualDir.getAllFiles(); + console.log(files); + }); + + it('should validate existing files', () => { + expect( + virtualDir.isValidFile('components/common/Button/index.tsx'), + ).toBeTruthy(); + expect( + virtualDir.isValidFile('components/layout/Sidebar/index.tsx'), + ).toBeTruthy(); + expect(virtualDir.isValidFile('nonexistent.ts')).toBeFalsy(); + }); + + it('should validate existing directories', () => { + expect(virtualDir.isValidDirectory('components/common')).toBeTruthy(); + expect(virtualDir.isValidDirectory('api')).toBeTruthy(); + expect(virtualDir.isValidDirectory('nonexistent')).toBeFalsy(); + }); + + it('should resolve relative paths correctly', () => { + const resolved = virtualDir.resolveRelativePath( + 'components/common/Button/index.tsx', + '../Input/index.tsx', + ); + expect(virtualDir.isValidFile(resolved)).toBeTruthy(); + expect(resolved).toBe('components/common/Input/index.tsx'); + }); + }); +}); diff --git a/backend/src/build-system/node/file-generate/Virtual-Directory.ts b/backend/src/build-system/node/file-generate/Virtual-Directory.ts new file mode 100644 index 00000000..6242b6d2 --- /dev/null +++ b/backend/src/build-system/node/file-generate/Virtual-Directory.ts @@ -0,0 +1,152 @@ +import * as path from 'path'; + +interface VirtualNode { + name: string; + isFile: boolean; + children: Map; +} + +export class VirtualDirectory { + private root: VirtualNode; + + constructor(structureMarkdown: string) { + this.root = { + name: 'src', + isFile: false, + children: new Map(), + }; + this.parseMarkdownStructure(structureMarkdown); + } + + private parseMarkdownStructure(markdownContent: string): void { + const lines = markdownContent.split('\n'); + const currentPath: VirtualNode[] = [this.root]; + const lastItemAtLevel: { [level: number]: boolean } = {}; + + for (let line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('FolderStructure')) continue; + + line = line.split('#')[0].trim(); + if (!line) continue; + + const match = line.match(/^([\s│]*)([├└])──\s*(.+)$/); + if (!match) continue; + + const prefix = match[1]; + const isLastItem = match[2] === '└'; + const afterBranch = match[3].trim(); + + if (!afterBranch || afterBranch === 'src/') continue; + + // Count vertical bars, accounting for spaces where last items were + const level = prefix.split('').reduce((count, char, index) => { + if ( + char === '│' || + (char === ' ' && lastItemAtLevel[Math.floor(index / 4)]) + ) { + return count + (index % 4 === 0 ? 1 : 0); + } + return count; + }, 0); + + // Update last item tracking + lastItemAtLevel[level] = isLastItem; + + const cleanName = afterBranch.replace(/\/$/, ''); + const isFile = path.extname(cleanName).length > 0; + + while (currentPath.length > level + 1) { + currentPath.pop(); + } + + const newNode: VirtualNode = { + name: cleanName, + isFile, + children: new Map(), + }; + + const parent = currentPath[currentPath.length - 1]; + parent.children.set(cleanName, newNode); + + if (!isFile) { + currentPath.push(newNode); + } + } + } + + printTree(): void { + const traverse = ( + node: VirtualNode, + depth: number = 0, + path: string = '', + ) => { + const indent = ' '.repeat(depth); + const currentPath = path ? `${path}/${node.name}` : node.name; + console.log( + `${indent}${node.name} (${node.isFile ? 'File' : 'Directory'})`, + ); + console.log(`${indent}Path: ${currentPath}`); + console.log(`${indent}Children: ${node.children.size}`); + + node.children.forEach((child) => { + traverse(child, depth + 1, currentPath); + }); + }; + + traverse(this.root); + } + + isValidFile(filePath: string): boolean { + const node = this.findNode(filePath); + return node?.isFile ?? false; + } + + isValidDirectory(dirPath: string): boolean { + const node = this.findNode(dirPath); + return node !== null && !node.isFile; + } + + private findNode(inputPath: string): VirtualNode | null { + const normalizedPath = this.normalizePath(inputPath); + const parts = normalizedPath.split('/').filter(Boolean); + + let current = this.root; + for (const part of parts) { + const next = current.children.get(part); + if (!next) return null; + current = next; + } + return current; + } + + private normalizePath(inputPath: string): string { + const withoutSrc = inputPath.startsWith('src/') + ? inputPath.slice(4) + : inputPath; + return path.normalize(withoutSrc).replace(/\\/g, '/').replace(/\/$/, ''); + } + + resolveRelativePath(fromFile: string, toPath: string): string { + const fromDir = path.dirname(fromFile); + const resolvedPath = path.join(fromDir, toPath).replace(/\\/g, '/'); + return this.normalizePath(resolvedPath); + } + + getAllFiles(): string[] { + const files: string[] = []; + + const traverse = (node: VirtualNode, parentPath: string = '') => { + for (const [name, child] of node.children) { + const currentPath = parentPath ? `${parentPath}/${name}` : name; + if (child.isFile) { + files.push(`src/${currentPath}`); // Prepend 'src/' to match the full path + } + traverse(child, currentPath); + } + }; + + traverse(this.root); + return files.sort(); + } +} From 6475140c2308847b17573ffb141de75124309729 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Fri, 6 Dec 2024 15:02:56 -0500 Subject: [PATCH 17/40] make sure src must be the start --- .../__tests__/testVirtualDir.spec.ts | 29 +++++++++------ .../node/file-generate/Virtual-Directory.ts | 37 ++++--------------- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/backend/src/build-system/__tests__/testVirtualDir.spec.ts b/backend/src/build-system/__tests__/testVirtualDir.spec.ts index 66f74c84..656c79f2 100644 --- a/backend/src/build-system/__tests__/testVirtualDir.spec.ts +++ b/backend/src/build-system/__tests__/testVirtualDir.spec.ts @@ -31,28 +31,35 @@ describe('FileGeneratorHandler and VirtualDirectory', () => { }); it('should validate existing files', () => { + expect( + virtualDir.isValidFile('src/components/common/Button/index.tsx'), + ).toBeTruthy(); expect( virtualDir.isValidFile('components/common/Button/index.tsx'), + ).toBeFalsy(); + expect( + virtualDir.isValidFile('src/components/layout/Sidebar/index.css'), ).toBeTruthy(); expect( virtualDir.isValidFile('components/layout/Sidebar/index.tsx'), - ).toBeTruthy(); + ).toBeFalsy(); expect(virtualDir.isValidFile('nonexistent.ts')).toBeFalsy(); }); it('should validate existing directories', () => { - expect(virtualDir.isValidDirectory('components/common')).toBeTruthy(); - expect(virtualDir.isValidDirectory('api')).toBeTruthy(); + expect(virtualDir.isValidDirectory('src')).toBeTruthy(); + expect(virtualDir.isValidDirectory('src/components/common')).toBeTruthy(); + expect(virtualDir.isValidDirectory('api')).toBeFalsy(); expect(virtualDir.isValidDirectory('nonexistent')).toBeFalsy(); }); - it('should resolve relative paths correctly', () => { - const resolved = virtualDir.resolveRelativePath( - 'components/common/Button/index.tsx', - '../Input/index.tsx', - ); - expect(virtualDir.isValidFile(resolved)).toBeTruthy(); - expect(resolved).toBe('components/common/Input/index.tsx'); - }); + // it('should resolve relative paths correctly', () => { + // const resolved = virtualDir.resolveRelativePath( + // 'components/common/Button/index.tsx', + // '../Input/index.tsx', + // ); + // expect(virtualDir.isValidFile(resolved)).toBeTruthy(); + // expect(resolved).toBe('components/common/Input/index.tsx'); + // }); }); }); diff --git a/backend/src/build-system/node/file-generate/Virtual-Directory.ts b/backend/src/build-system/node/file-generate/Virtual-Directory.ts index 6242b6d2..ec2485c4 100644 --- a/backend/src/build-system/node/file-generate/Virtual-Directory.ts +++ b/backend/src/build-system/node/file-generate/Virtual-Directory.ts @@ -75,28 +75,6 @@ export class VirtualDirectory { } } - printTree(): void { - const traverse = ( - node: VirtualNode, - depth: number = 0, - path: string = '', - ) => { - const indent = ' '.repeat(depth); - const currentPath = path ? `${path}/${node.name}` : node.name; - console.log( - `${indent}${node.name} (${node.isFile ? 'File' : 'Directory'})`, - ); - console.log(`${indent}Path: ${currentPath}`); - console.log(`${indent}Children: ${node.children.size}`); - - node.children.forEach((child) => { - traverse(child, depth + 1, currentPath); - }); - }; - - traverse(this.root); - } - isValidFile(filePath: string): boolean { const node = this.findNode(filePath); return node?.isFile ?? false; @@ -111,9 +89,14 @@ export class VirtualDirectory { const normalizedPath = this.normalizePath(inputPath); const parts = normalizedPath.split('/').filter(Boolean); + // Require src prefix + if (parts[0] !== 'src') { + return null; + } + let current = this.root; - for (const part of parts) { - const next = current.children.get(part); + for (let i = 1; i < parts.length; i++) { + const next = current.children.get(parts[i]); if (!next) return null; current = next; } @@ -121,12 +104,8 @@ export class VirtualDirectory { } private normalizePath(inputPath: string): string { - const withoutSrc = inputPath.startsWith('src/') - ? inputPath.slice(4) - : inputPath; - return path.normalize(withoutSrc).replace(/\\/g, '/').replace(/\/$/, ''); + return path.normalize(inputPath).replace(/\\/g, '/').replace(/\/$/, ''); } - resolveRelativePath(fromFile: string, toPath: string): string { const fromDir = path.dirname(fromFile); const resolvedPath = path.join(fromDir, toPath).replace(/\\/g, '/'); From 5025dc60866f5f262366b347f43f39f4d0c9b7f5 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Fri, 6 Dec 2024 20:42:53 -0500 Subject: [PATCH 18/40] update test case --- .../__tests__/testVirtualDir.spec.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/build-system/__tests__/testVirtualDir.spec.ts b/backend/src/build-system/__tests__/testVirtualDir.spec.ts index 656c79f2..a00424bf 100644 --- a/backend/src/build-system/__tests__/testVirtualDir.spec.ts +++ b/backend/src/build-system/__tests__/testVirtualDir.spec.ts @@ -1,7 +1,7 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import { FileGeneratorHandler } from '../node/file-generate'; -import { VirtualDirectory } from '../node/file-generate/Virtual-Directory'; +import { VirtualDirectory } from '../node/file-generate/virtualDirectory'; describe('FileGeneratorHandler and VirtualDirectory', () => { const structMdFilePath = path.resolve( @@ -53,13 +53,13 @@ describe('FileGeneratorHandler and VirtualDirectory', () => { expect(virtualDir.isValidDirectory('nonexistent')).toBeFalsy(); }); - // it('should resolve relative paths correctly', () => { - // const resolved = virtualDir.resolveRelativePath( - // 'components/common/Button/index.tsx', - // '../Input/index.tsx', - // ); - // expect(virtualDir.isValidFile(resolved)).toBeTruthy(); - // expect(resolved).toBe('components/common/Input/index.tsx'); - // }); + it('should resolve relative paths correctly', () => { + const resolved = virtualDir.resolveRelativePath( + 'src/components/common/Button/index.tsx', + '../Input/index.tsx', + ); + expect(virtualDir.isValidFile(resolved)).toBeTruthy(); + expect(resolved).toBe('src/components/common/Input/index.tsx'); + }); }); }); From ea49c31822ca73a6c810876e3dd289aedbdb5265 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Fri, 6 Dec 2024 22:45:22 -0500 Subject: [PATCH 19/40] add VirtualDirectory to file generater to check file --- .../__tests__/test-file-create.spec.ts | 38 ++++++++++--------- .../build-system/node/file-generate/index.ts | 30 +++++++++++++++ 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/backend/src/build-system/__tests__/test-file-create.spec.ts b/backend/src/build-system/__tests__/test-file-create.spec.ts index 3e40f68f..5c195c25 100644 --- a/backend/src/build-system/__tests__/test-file-create.spec.ts +++ b/backend/src/build-system/__tests__/test-file-create.spec.ts @@ -4,6 +4,11 @@ import { FileGeneratorHandler } from '../node/file-generate'; // Update with act describe('FileGeneratorHandler', () => { const projectSrcPath = 'src\\build-system\\__tests__\\test-project\\'; + // Read JSON data from file + const mdFilePath = path.resolve('src\\build-system\\__tests__\\file-arch.md'); + const structMdFilePath = path.resolve( + 'src\\build-system\\__tests__\\file-structure-document.md', + ); beforeEach(async () => { // Ensure the project directory is clean @@ -15,32 +20,31 @@ describe('FileGeneratorHandler', () => { await fs.remove('src\\build-system\\__tests__\\test-project\\src\\'); }); - it('should generate files based on file-arch.json', async () => { - const handler = new FileGeneratorHandler(); - - // Read JSON data from file - const mdFilePath = path.resolve( - 'src\\build-system\\__tests__\\file-arch.md', + it('should generate files based on file-arch.md', async () => { + const archMarkdownContent = fs.readFileSync( + path.resolve(mdFilePath), + 'utf8', + ); + const structMarkdownContent = fs.readFileSync( + path.resolve(structMdFilePath), + 'utf8', ); - const markdownContent = fs.readFileSync(path.resolve(mdFilePath), 'utf8'); + const handler = new FileGeneratorHandler(structMarkdownContent); // Run the file generator with the JSON data - const result = await handler.generateFiles(markdownContent, projectSrcPath); - - // Verify files were generated correctly - // const generatedFiles = Object.keys(jsonData.files); - // for (const fileName of generatedFiles) { - // const filePath = path.resolve(projectSrcPath, fileName); - // const fileExists = await fs.pathExists(filePath); - // expect(fileExists).toBe(true); - // } + const result = await handler.generateFiles( + archMarkdownContent, + projectSrcPath, + ); console.log('File generation result:', result); // Verify that all files exist const jsonData = JSON.parse( - /([\s\S]*?)<\/GENERATEDCODE>/.exec(markdownContent)![1], + /([\s\S]*?)<\/GENERATEDCODE>/.exec( + archMarkdownContent, + )![1], ); const files = Object.keys(jsonData.files); diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index c9b8bc79..0737f7e7 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -2,9 +2,15 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { Logger } from '@nestjs/common'; import * as toposort from 'toposort'; +import { VirtualDirectory } from './virtual-directory'; export class FileGeneratorHandler { private readonly logger = new Logger('FileGeneratorHandler'); + private virtualDir: VirtualDirectory; + + constructor(structureMarkdown: string) { + this.virtualDir = new VirtualDirectory(structureMarkdown); + } /** * Generate files based on the JSON extracted from a Markdown file. @@ -22,6 +28,9 @@ export class FileGeneratorHandler { const { graph, nodes } = this.buildDependencyGraph(jsonData); this.detectCycles(graph); + // Add virtual directory validation + this.validateAgainstVirtualDirectory(nodes); + // After validation and cycle detection, perform topological sort const sortedFiles = this.getSortedFiles(graph, nodes); @@ -177,6 +186,27 @@ export class FileGeneratorHandler { } } + /** + * Validate that all files and dependencies exist in the virtual directory structure + * @param nodes Set of all files and dependencies + * @throws Error if any file or dependency is not found in the virtual directory + */ + private validateAgainstVirtualDirectory(nodes: Set): void { + const invalidFiles: string[] = []; + + nodes.forEach((filePath) => { + if (!this.virtualDir.isValidFile(filePath)) { + invalidFiles.push(filePath); + } + }); + + if (invalidFiles.length > 0) { + throw new Error( + `The following files do not exist in the project structure:\n${invalidFiles.join('\n')}`, + ); + } + } + /** * Create a file, including creating necessary directories. * @param filePath The full path of the file to create. From 8b697dc1448d7c8ee628fe7db2eef511eae1dd31 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 8 Dec 2024 20:21:51 -0500 Subject: [PATCH 20/40] Force page name stype --- backend/src/build-system/node/ux-sitemap-structure/prompt.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/build-system/node/ux-sitemap-structure/prompt.ts b/backend/src/build-system/node/ux-sitemap-structure/prompt.ts index 0e6f9322..933bb8ae 100644 --- a/backend/src/build-system/node/ux-sitemap-structure/prompt.ts +++ b/backend/src/build-system/node/ux-sitemap-structure/prompt.ts @@ -18,12 +18,13 @@ export const prompts = { 2, You need to ensure all features from the sitemap documentation are addressed. 3, You need to identify and define every page/screen required for the application. 4, Detailed Breakdown for Each Page/Screen: + Page name: ## {index}. Name page Page Purpose: Clearly state the user goal for the page. Core Elements: List all components (e.g., headers, buttons, sidebars) and explain their role on the page. Include specific interactions and behaviors for each element. Content Display: - Identify the information that needs to be visible on the page and why it’s essential for the user. + Identify the information that needs to be visible on the page and why it's essential for the user. Navigation and Routes: Specify all frontend routes required to navigate to this page. Include links or actions that lead to other pages or states. From 531ef299ae7b575eea0337fad0ddd63d084478eb Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 8 Dec 2024 20:33:40 -0500 Subject: [PATCH 21/40] ask gpt to self check --- .../node/frontend-file-structure/prompt.ts | 64 +++++++++++++++++-- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/backend/src/build-system/node/frontend-file-structure/prompt.ts b/backend/src/build-system/node/frontend-file-structure/prompt.ts index b8a445dd..6929733d 100644 --- a/backend/src/build-system/node/frontend-file-structure/prompt.ts +++ b/backend/src/build-system/node/frontend-file-structure/prompt.ts @@ -30,13 +30,11 @@ Include: Add example filenames for components, hooks, APIs, etc. Do Not Include: - Asset folders (e.g., images, icons, fonts). Test folders or files. Service folders unrelated to API logic. File Naming Guidelines: - Use meaningful and descriptive file names. For components, include an index.tsx file in each folder to simplify imports. Each component should have its own folder named after the component (e.g., Button/). @@ -44,11 +42,14 @@ File Naming Guidelines: Component-specific styles must be in index.css within the same folder as the component. File Comments: - Include comments describing the purpose of each file or folder to improve readability. -Self check: +Ask yourself: 1, Are you consider all the cases based on the sitemap doc? If not add new folder or file + 2, Are you consider all the components based on the sitemap doc? If not add new folder or file + 3, Are you consider all the hooks based on the sitemap doc? If not add new folder or file + 4, Are you consider all the api based on the sitemap doc? If not add new folder or file + 5, Are you consider all the pages based on the sitemap doc? If not add new folder or file This final result must be 100% complete. Will be directly use in the production @@ -56,9 +57,62 @@ Output Format: Start with: "\`\`\`FolderStructure" Tree format: - Include folder names with placeholder/example files inside. + Include folder names with placeholder files inside. Add comments to describe the purpose of each file/folder. End with: "\`\`\`" `; }, + convertTreeToJsonPrompt: (treeMarkdown: string): string => { + return `You are a highly skilled developer. Your task is to convert the given file and folder structure, currently represented in an ASCII tree format, into a JSON structure. The JSON structure must: + + - Represent directories and files in a hierarchical manner. + - Use objects with "type" and "name" keys. + - For directories: + - "type": "directory" + - "name": "" + - "children": [ ... ] (an array of files or directories) + - For files: + - "type": "file" + - "name": "" + - Maintain the same nesting as the original ASCII tree. + + **Input Tree:** + \`\`\` + ${treeMarkdown} + \`\`\` + + **Output Format:** + Return a JSON object of the form: + \`\`\`json + { + "type": "directory", + "name": "", + "children": [ + { + "type": "directory", + "name": "subDirName", + "children": [ + { + "type": "file", + "name": "fileName.ext" + } + ] + }, + { + "type": "file", + "name": "anotherFile.ext" + } + ] + } + \`\`\` + + **Additional Rules:** + - Keep directory names and file names exactly as they appear (excluding trailing slashes). + - For directories that appear like "common/", in the JSON just use "common" as the name. + - Do not include comments or extra fields besides "type", "name", and "children". + - The root node should correspond to the top-level directory in the tree. + + Return only the JSON structure (no explanations, no additional comments). This JSON will be used directly in the application. + `; + }, }; From 57945056a68493ab730e8b52e1e8a5b2e2dba885 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Sun, 8 Dec 2024 22:47:26 -0500 Subject: [PATCH 22/40] add step for transfer tree to json --- .../node/frontend-file-structure/index.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/src/build-system/node/frontend-file-structure/index.ts b/backend/src/build-system/node/frontend-file-structure/index.ts index bd277929..2d149abf 100644 --- a/backend/src/build-system/node/frontend-file-structure/index.ts +++ b/backend/src/build-system/node/frontend-file-structure/index.ts @@ -9,7 +9,7 @@ export class FileStructureHandler implements BuildHandler { private readonly logger: Logger = new Logger('FileStructureHandler'); async run(context: BuilderContext, args: unknown): Promise { - console.log('Generating File Structure Document...'); + this.logger.log('Generating File Structure Document...'); // extract relevant data from the context const projectName = @@ -38,9 +38,21 @@ export class FileStructureHandler implements BuildHandler { }, 'gpt-4o-mini', ); + + this.logger.log('For fileStructureContent debug: ' + fileStructureContent); + + const ToJsonPrompt = prompts.convertTreeToJsonPrompt(fileStructureContent); + + const fileStructureJsonContent = await context.model.chatSync( + { + content: ToJsonPrompt, + }, + 'gpt-4o-mini', + ); + return { success: true, - data: fileStructureContent, + data: fileStructureJsonContent, }; } } From a32ca2617ad2676a2b2fbf5f745f92ace8d9da5a Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 12:06:15 -0500 Subject: [PATCH 23/40] update to use json --- .../node/file-generate/Virtual-Directory.ts | 83 +++++++------------ 1 file changed, 29 insertions(+), 54 deletions(-) diff --git a/backend/src/build-system/node/file-generate/Virtual-Directory.ts b/backend/src/build-system/node/file-generate/Virtual-Directory.ts index ec2485c4..b11b7be0 100644 --- a/backend/src/build-system/node/file-generate/Virtual-Directory.ts +++ b/backend/src/build-system/node/file-generate/Virtual-Directory.ts @@ -9,68 +9,43 @@ interface VirtualNode { export class VirtualDirectory { private root: VirtualNode; - constructor(structureMarkdown: string) { + constructor(jsonStructure: string) { this.root = { name: 'src', isFile: false, children: new Map(), }; - this.parseMarkdownStructure(structureMarkdown); + this.parseJsonStructure(jsonStructure); } - private parseMarkdownStructure(markdownContent: string): void { - const lines = markdownContent.split('\n'); - const currentPath: VirtualNode[] = [this.root]; - const lastItemAtLevel: { [level: number]: boolean } = {}; - - for (let line of lines) { - const trimmed = line.trim(); - if (!trimmed || trimmed.startsWith('FolderStructure')) continue; - - line = line.split('#')[0].trim(); - if (!line) continue; - - const match = line.match(/^([\s│]*)([├└])──\s*(.+)$/); - if (!match) continue; - - const prefix = match[1]; - const isLastItem = match[2] === '└'; - const afterBranch = match[3].trim(); + private cleanJsonContent(content: string): string { + const jsonStart = content.indexOf('{'); + return content.slice(jsonStart); + } - if (!afterBranch || afterBranch === 'src/') continue; + private parseJsonStructure(jsonContent: string): void { + try { + const cleanedJson = this.cleanJsonContent(jsonContent); + const structure = JSON.parse(cleanedJson); + this.buildTree(structure, this.root); + } catch (error) { + console.error('Failed to parse JSON structure:', error); + } + } - // Count vertical bars, accounting for spaces where last items were - const level = prefix.split('').reduce((count, char, index) => { - if ( - char === '│' || - (char === ' ' && lastItemAtLevel[Math.floor(index / 4)]) - ) { - return count + (index % 4 === 0 ? 1 : 0); + private buildTree(node: FileStructureNode, virtualNode: VirtualNode): void { + if (node.children) { + for (const child of node.children) { + const newNode: VirtualNode = { + name: child.name, + isFile: child.type === 'file', + children: new Map(), + }; + virtualNode.children.set(child.name, newNode); + + if (child.type === 'directory' && child.children) { + this.buildTree(child, newNode); } - return count; - }, 0); - - // Update last item tracking - lastItemAtLevel[level] = isLastItem; - - const cleanName = afterBranch.replace(/\/$/, ''); - const isFile = path.extname(cleanName).length > 0; - - while (currentPath.length > level + 1) { - currentPath.pop(); - } - - const newNode: VirtualNode = { - name: cleanName, - isFile, - children: new Map(), - }; - - const parent = currentPath[currentPath.length - 1]; - parent.children.set(cleanName, newNode); - - if (!isFile) { - currentPath.push(newNode); } } } @@ -89,7 +64,6 @@ export class VirtualDirectory { const normalizedPath = this.normalizePath(inputPath); const parts = normalizedPath.split('/').filter(Boolean); - // Require src prefix if (parts[0] !== 'src') { return null; } @@ -106,6 +80,7 @@ export class VirtualDirectory { private normalizePath(inputPath: string): string { return path.normalize(inputPath).replace(/\\/g, '/').replace(/\/$/, ''); } + resolveRelativePath(fromFile: string, toPath: string): string { const fromDir = path.dirname(fromFile); const resolvedPath = path.join(fromDir, toPath).replace(/\\/g, '/'); @@ -119,7 +94,7 @@ export class VirtualDirectory { for (const [name, child] of node.children) { const currentPath = parentPath ? `${parentPath}/${name}` : name; if (child.isFile) { - files.push(`src/${currentPath}`); // Prepend 'src/' to match the full path + files.push(`src/${currentPath}`); } traverse(child, currentPath); } From 93dc02621739974ccaeaf081a94d95892bf5b072 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 12:06:42 -0500 Subject: [PATCH 24/40] use gpt-40-mini --- backend/src/build-system/node/ux-sitemap-document/uxsmd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/build-system/node/ux-sitemap-document/uxsmd.ts b/backend/src/build-system/node/ux-sitemap-document/uxsmd.ts index 499f15e4..42d6fce6 100644 --- a/backend/src/build-system/node/ux-sitemap-document/uxsmd.ts +++ b/backend/src/build-system/node/ux-sitemap-document/uxsmd.ts @@ -38,7 +38,7 @@ export class UXSMDHandler implements BuildHandler { private async generateUXSMDFromLLM(prompt: string): Promise { const modelProvider = ModelProvider.getInstance(); - const model = 'gpt-3.5-turbo'; + const model = 'gpt-4o-mini'; const prdContent = modelProvider.chatSync( { From 2873bafa729d40f225d90a688fb84f539ea380c4 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 12:07:15 -0500 Subject: [PATCH 25/40] fix error --- .../build-system/node/file-generate/Virtual-Directory.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/src/build-system/node/file-generate/Virtual-Directory.ts b/backend/src/build-system/node/file-generate/Virtual-Directory.ts index b11b7be0..fd316b2e 100644 --- a/backend/src/build-system/node/file-generate/Virtual-Directory.ts +++ b/backend/src/build-system/node/file-generate/Virtual-Directory.ts @@ -6,6 +6,12 @@ interface VirtualNode { children: Map; } +interface FileStructureNode { + type: 'file' | 'directory'; + name: string; + children?: FileStructureNode[]; +} + export class VirtualDirectory { private root: VirtualNode; From d3fcaaa2ba6857bb5539d46397d3686bf504712c Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 18:57:04 -0500 Subject: [PATCH 26/40] add run for tmp --- backend/src/build-system/node/file-generate/index.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index 0737f7e7..6cc95054 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -3,6 +3,8 @@ import * as path from 'path'; import { Logger } from '@nestjs/common'; import * as toposort from 'toposort'; import { VirtualDirectory } from './virtual-directory'; +import { BuilderContext } from 'src/build-system/context'; +import { BuildHandler, BuildResult } from 'src/build-system/types'; export class FileGeneratorHandler { private readonly logger = new Logger('FileGeneratorHandler'); @@ -12,6 +14,13 @@ export class FileGeneratorHandler { this.virtualDir = new VirtualDirectory(structureMarkdown); } + async run(context: BuilderContext, args: unknown): Promise { + return { + success: true, + data: null, + }; + } + /** * Generate files based on the JSON extracted from a Markdown file. * @param markdownContent The Markdown content containing the JSON. @@ -21,6 +30,7 @@ export class FileGeneratorHandler { markdownContent: string, projectSrcPath: string, ): Promise<{ success: boolean; data: string }> { + // add test for validate const jsonData = this.extractJsonFromMarkdown(markdownContent); this.validateJsonData(jsonData); @@ -38,6 +48,7 @@ export class FileGeneratorHandler { for (const file of sortedFiles) { const fullPath = path.resolve(projectSrcPath, file); this.logger.log(`Generating file in dependency order: ${fullPath}`); + // to do await this.createFile(fullPath); } From 09957bd565c60a476101d3007b147782145ee73f Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 22:01:04 -0500 Subject: [PATCH 27/40] add virtual dir to file struct --- backend/src/build-system/context.ts | 7 ++++ .../node/file-generate/Virtual-Directory.ts | 10 +++--- .../build-system/node/file-generate/index.ts | 2 +- .../node/frontend-file-structure/index.ts | 33 +++++++++++++++---- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/backend/src/build-system/context.ts b/backend/src/build-system/context.ts index 1e31d6f5..cfbb9381 100644 --- a/backend/src/build-system/context.ts +++ b/backend/src/build-system/context.ts @@ -7,6 +7,7 @@ import { BuildSequence, } from './types'; import { Logger } from '@nestjs/common'; +import { VirtualDirectory } from './node/file-generate/virtual-directory'; export type GlobalDataKeys = 'projectName' | 'description' | 'platform'; type ContextData = { @@ -26,6 +27,7 @@ export class BuilderContext { private results: Map = new Map(); private handlerManager: BuildHandlerManager; public model: ModelProvider; + private virtualDirectory: VirtualDirectory; constructor( private sequence: BuildSequence, @@ -34,6 +36,7 @@ export class BuilderContext { this.handlerManager = BuildHandlerManager.getInstance(); this.model = ModelProvider.getInstance(); this.logger = new Logger(`builder-context-${id}`); + this.virtualDirectory = new VirtualDirectory(); } canExecute(nodeId: string): boolean { @@ -106,6 +109,10 @@ export class BuilderContext { return this.results.get(nodeId); } + buildVirtualDirectory(jsonContent: string): boolean { + return this.virtualDirectory.parseJsonStructure(jsonContent); + } + private async executeNode( node: BuildNode, args: unknown, diff --git a/backend/src/build-system/node/file-generate/Virtual-Directory.ts b/backend/src/build-system/node/file-generate/Virtual-Directory.ts index fd316b2e..c5b96326 100644 --- a/backend/src/build-system/node/file-generate/Virtual-Directory.ts +++ b/backend/src/build-system/node/file-generate/Virtual-Directory.ts @@ -15,27 +15,29 @@ interface FileStructureNode { export class VirtualDirectory { private root: VirtualNode; - constructor(jsonStructure: string) { + constructor() { this.root = { name: 'src', isFile: false, children: new Map(), }; - this.parseJsonStructure(jsonStructure); } private cleanJsonContent(content: string): string { const jsonStart = content.indexOf('{'); - return content.slice(jsonStart); + const jsonEnd = content.lastIndexOf('}'); + return content.slice(jsonStart, jsonEnd + 1); } - private parseJsonStructure(jsonContent: string): void { + public parseJsonStructure(jsonContent: string): boolean { try { const cleanedJson = this.cleanJsonContent(jsonContent); const structure = JSON.parse(cleanedJson); this.buildTree(structure, this.root); + return true; } catch (error) { console.error('Failed to parse JSON structure:', error); + return false; } } diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index 6cc95054..556f421d 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -11,7 +11,7 @@ export class FileGeneratorHandler { private virtualDir: VirtualDirectory; constructor(structureMarkdown: string) { - this.virtualDir = new VirtualDirectory(structureMarkdown); + this.virtualDir = new VirtualDirectory(); } async run(context: BuilderContext, args: unknown): Promise { diff --git a/backend/src/build-system/node/frontend-file-structure/index.ts b/backend/src/build-system/node/frontend-file-structure/index.ts index 2d149abf..63fa13b0 100644 --- a/backend/src/build-system/node/frontend-file-structure/index.ts +++ b/backend/src/build-system/node/frontend-file-structure/index.ts @@ -32,6 +32,7 @@ export class FileStructureHandler implements BuildHandler { 'FrameWork Holder', ); + // Call the chatSync function to get the file structure content. const fileStructureContent = await context.model.chatSync( { content: prompt, @@ -43,12 +44,32 @@ export class FileStructureHandler implements BuildHandler { const ToJsonPrompt = prompts.convertTreeToJsonPrompt(fileStructureContent); - const fileStructureJsonContent = await context.model.chatSync( - { - content: ToJsonPrompt, - }, - 'gpt-4o-mini', - ); + // Try to build the virtual directory from the JSON structure. + let successBuild = false; + let fileStructureJsonContent = null; + let retry = 0; + const retryChances = 2; + while (!successBuild) { + if (retry > retryChances) { + throw new Error( + 'Failed to build virtual directory after multiple attempts', + ); + } + fileStructureJsonContent = await context.model.chatSync( + { + content: ToJsonPrompt, + }, + 'gpt-4o-mini', + ); + + this.logger.log('fileStructureJsonContent: ' + fileStructureJsonContent); + + successBuild = context.buildVirtualDirectory(fileStructureJsonContent); + retry += 1; + } + + this.logger.log('fileStructureJsonContent success'); + this.logger.log('buildVirtualDirectory success'); return { success: true, From 3adcd0506bd7054351ff7bb78d96788d53fc004f Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 22:11:21 -0500 Subject: [PATCH 28/40] fix error --- backend/src/build-system/context.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/backend/src/build-system/context.ts b/backend/src/build-system/context.ts index cfbb9381..d1354cda 100644 --- a/backend/src/build-system/context.ts +++ b/backend/src/build-system/context.ts @@ -41,13 +41,24 @@ export class BuilderContext { canExecute(nodeId: string): boolean { const node = this.findNode(nodeId); + if (!node) return false; if (this.state.completed.has(nodeId) || this.state.pending.has(nodeId)) { + console.log(`Node ${nodeId} is already completed or pending.`); + return false; + } + + const unmetDependencies = + node.requires?.filter((dep) => !this.state.completed.has(dep)) || []; + if (unmetDependencies.length > 0) { + console.log( + `Node ${nodeId} has unmet dependencies: ${unmetDependencies}`, + ); return false; } - return !node.requires?.some((dep) => !this.state.completed.has(dep)); + return true; } private findNode(nodeId: string): BuildNode | null { @@ -63,7 +74,8 @@ export class BuilderContext { if (!node) { throw new Error(`Node not found: ${nodeId}`); } - + console.log('canExecute id: ' + node.name); + console.log('canExecute: ' + this.canExecute(nodeId)); if (!this.canExecute(nodeId)) { throw new Error(`Dependencies not met for node: ${nodeId}`); } @@ -117,10 +129,10 @@ export class BuilderContext { node: BuildNode, args: unknown, ): Promise { - if (process.env.NODE_ENV === 'test') { - this.logger.log(`[TEST] Executing node: ${node.id}`); - return { success: true, data: { nodeId: node.id } }; - } + // if (process.env.NODE_ENV === 'test') { + // this.logger.log(`[TEST] Executing node: ${node.id}`); + // return { success: true, data: { nodeId: node.id } }; + // } this.logger.log(`Executing node: ${node.id}`); const handler = this.handlerManager.getHandler(node.id); From 1641a3dcef25a365d18db91f0342eb697d705627 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 22:20:19 -0500 Subject: [PATCH 29/40] add placeholder for run --- backend/src/build-system/node/file-generate/index.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index 556f421d..bfd3495a 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -10,14 +10,20 @@ export class FileGeneratorHandler { private readonly logger = new Logger('FileGeneratorHandler'); private virtualDir: VirtualDirectory; - constructor(structureMarkdown: string) { + constructor() { this.virtualDir = new VirtualDirectory(); } async run(context: BuilderContext, args: unknown): Promise { + const fileArch = args[0] as string; + + // change here + const projectSrcPath = ''; + this.generateFiles(JSON.stringify(fileArch, null, 2), projectSrcPath); + return { success: true, - data: null, + data: 'Files and dependencies created successfully.', }; } From aad4f87a7d85b791bc6b048164ad92207ff3765c Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 9 Dec 2024 22:21:08 -0500 Subject: [PATCH 30/40] update virutual dir test --- .../__tests__/testVirtualDir.spec.ts | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/backend/src/build-system/__tests__/testVirtualDir.spec.ts b/backend/src/build-system/__tests__/testVirtualDir.spec.ts index a00424bf..d7261bb7 100644 --- a/backend/src/build-system/__tests__/testVirtualDir.spec.ts +++ b/backend/src/build-system/__tests__/testVirtualDir.spec.ts @@ -1,7 +1,7 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import { FileGeneratorHandler } from '../node/file-generate'; -import { VirtualDirectory } from '../node/file-generate/virtualDirectory'; +import { VirtualDirectory } from '../node/file-generate/virtual-directory'; describe('FileGeneratorHandler and VirtualDirectory', () => { const structMdFilePath = path.resolve( @@ -22,7 +22,8 @@ describe('FileGeneratorHandler and VirtualDirectory', () => { beforeEach(() => { structMarkdownContent = fs.readFileSync(structMdFilePath, 'utf8'); - virtualDir = new VirtualDirectory(structMarkdownContent); + virtualDir = new VirtualDirectory(); + virtualDir.parseJsonStructure(structMarkdownContent); }); it('should print tree structure', () => { @@ -30,19 +31,19 @@ describe('FileGeneratorHandler and VirtualDirectory', () => { console.log(files); }); + // change test path to your current test file it('should validate existing files', () => { - expect( - virtualDir.isValidFile('src/components/common/Button/index.tsx'), - ).toBeTruthy(); - expect( - virtualDir.isValidFile('components/common/Button/index.tsx'), - ).toBeFalsy(); - expect( - virtualDir.isValidFile('src/components/layout/Sidebar/index.css'), - ).toBeTruthy(); - expect( - virtualDir.isValidFile('components/layout/Sidebar/index.tsx'), - ).toBeFalsy(); + expect(virtualDir.isValidFile('src/pages/Home/index.tsx')).toBeTruthy(); + // expect(virtualDir.isValidFile('src/utils/validators.ts')).toBeTruthy(); + // expect( + // virtualDir.isValidFile('components/common/Button/index.tsx'), + // ).toBeFalsy(); + // expect( + // virtualDir.isValidFile('src/components/layout/Footer/index.css'), + // ).toBeTruthy(); + // expect( + // virtualDir.isValidFile('components/layout/Footer/index.tsx'), + // ).toBeFalsy(); expect(virtualDir.isValidFile('nonexistent.ts')).toBeFalsy(); }); @@ -56,10 +57,10 @@ describe('FileGeneratorHandler and VirtualDirectory', () => { it('should resolve relative paths correctly', () => { const resolved = virtualDir.resolveRelativePath( 'src/components/common/Button/index.tsx', - '../Input/index.tsx', + '../Loader/index.tsx', ); expect(virtualDir.isValidFile(resolved)).toBeTruthy(); - expect(resolved).toBe('src/components/common/Input/index.tsx'); + expect(resolved).toBe('src/components/common/Loader/index.tsx'); }); }); }); From bf3030d612198a45fe4158fa6e77cf69ce212f1d Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 15:40:05 -0500 Subject: [PATCH 31/40] lets remove all mock markdown file --- .../__tests__/datamap-structure.md | 275 ------------------ .../__tests__/db-requirement-document.md | 230 --------------- .../src/build-system/__tests__/file-arch.md | 129 -------- .../__tests__/file-structure-document.md | 77 ----- 4 files changed, 711 deletions(-) delete mode 100644 backend/src/build-system/__tests__/datamap-structure.md delete mode 100644 backend/src/build-system/__tests__/db-requirement-document.md delete mode 100644 backend/src/build-system/__tests__/file-arch.md delete mode 100644 backend/src/build-system/__tests__/file-structure-document.md diff --git a/backend/src/build-system/__tests__/datamap-structure.md b/backend/src/build-system/__tests__/datamap-structure.md deleted file mode 100644 index 6141554e..00000000 --- a/backend/src/build-system/__tests__/datamap-structure.md +++ /dev/null @@ -1,275 +0,0 @@ -# Detailed UX Structure Map: Spotify-like Music Web Application - -## **1. Home Page** - -### Page Purpose - -Provide users with quick access to featured playlists, personalized recommendations, new releases, and music genres. - -### Core Elements - -- **Header**: - - Logo: Navigate back to the home page. - - Search Bar: Quick access to search functionality. - - Navigation Links: Home, Search, Library, Account. -- **Main Content Area**: - - **Featured Playlists**: Grid of curated playlists with cover images and titles. - - Interactions: Click to navigate to the playlist page. - - **Personalized Recommendations**: Horizontal scroll carousel of songs and albums tailored to the user. - - Dynamic Content: Updates based on user listening history. - - **New Releases**: Grid displaying recently released albums or singles. - - Interactions: Click to open album details. - - **Genres**: List of genres represented as clickable tiles. -- **Footer**: - - Persistent music player bar with play/pause, skip, and volume controls. - -### Content Display - -- Visual hierarchy prioritizing featured content and recommendations. -- Dynamic updates to reflect user preferences and real-time data. - -### Navigation and Routes - -- `/home`: Main route for the home page. -- Links to `/playlist/:id`, `/album/:id`, `/genre/:id`. - -### Restrictions - -- Requires user login to display personalized recommendations. - ---- - -## **2. Search Page** - -### Page Purpose - -Allow users to search for specific songs, albums, artists, or playlists and view categorized results. - -### Core Elements - -- **Header**: - - Logo, Navigation Links, and Search Bar (persistent from Home Page). -- **Search Results Section**: - - **Songs**: List of matching songs with play buttons. - - **Albums**: Grid of albums with cover art and titles. - - **Artists**: Horizontal scroll carousel of artists. - - **Playlists**: List of curated and user-created playlists. -- **No Results Message**: Display when no matching content is found. - -### Content Display - -- Results categorized and clearly separated. -- Real-time search suggestions displayed as user types. - -### Navigation and Routes - -- `/search`: Search page route. -- Links to `/song/:id`, `/album/:id`, `/artist/:id`, `/playlist/:id`. - -### Restrictions - -- Accessible without login but limited to generic content unless logged in. - ---- - -## **3. Library Page** - -### Page Purpose - -Provide users access to their playlists, liked songs, and recently played music. - -### Core Elements - -- **Header**: - - Persistent header with navigation and search. -- **Library Sections**: - - **My Playlists**: List of user-created playlists. - - Actions: Edit, delete, reorder. - - **Liked Songs**: List of favorited songs with play buttons. - - **Recently Played**: List of previously played songs and playlists. - -### Content Display - -- Organize content with clear sections and labels. -- Show metadata (e.g., song duration, artist) for user context. - -### Navigation and Routes - -- `/library`: Library main page route. -- Links to `/playlist/:id`, `/song/:id`. - -### Restrictions - -- Requires login to access user-specific content. - ---- - -## **4. Playlist/Album/Genre Pages** - -### Page Purpose - -Allow users to view and play content within a specific playlist, album, or genre. - -### Core Elements - -- **Header**: - - Back Button: Navigate to the previous page. - - Playlist/Album/Genre Name: Display prominently. -- **Content Section**: - - **Playlist/Album Info**: Cover art, title, creator name, description. - - **Track List**: List of songs with play buttons and metadata. - - **Action Buttons**: - - Play All: Start playing all tracks in order. - - Add to Library: Save playlist/album to user library (if applicable). -- **Footer**: - - Persistent music player bar. - -### Content Display - -- Highlight album or playlist cover and metadata for context. -- Easy access to play all tracks or individual songs. - -### Navigation and Routes - -- `/playlist/:id`, `/album/:id`, `/genre/:id`. - -### Restrictions - -- Accessible without login, but restricted to sample tracks if not logged in. - ---- - -## **5. Player Page (Now Playing)** - -### Page Purpose - -Provide full controls for the currently playing track. - -### Core Elements - -- **Header**: - - Back Button: Navigate to the previous page. -- **Main Player Controls**: - - Song Info: Display song title, artist, album cover. - - Play/Pause, Skip, Seek Bar, Volume Controls. -- **Lyrics Section**: - - Toggle to view lyrics for the current track (if available). -- **Queue Section**: - - List of upcoming tracks with reordering options. - -### Content Display - -- Centralized focus on the current track with controls easily accessible. - -### Navigation and Routes - -- `/player`. - -### Restrictions - -- Requires active playback session. - ---- - -## **6. Account Page** - -### Page Purpose - -Allow users to manage profile settings and preferences. - -### Core Elements - -- **Header**: - - Persistent navigation and search. -- **Profile Info Section**: - - Display user profile details (username, email). - - Edit button for updating profile. -- **Preferences Section**: - - Toggle for dark mode/light mode. - - Audio quality selection. -- **Logout Button**: - - Prominent button to securely log out. - -### Content Display - -- Clear sections for profile details and app preferences. -- Easy access to frequently updated settings. - -### Navigation and Routes - -- `/account`. - -### Restrictions - -- Requires login. - ---- - -## **7. Onboarding Pages** - -### Page Purpose - -Guide new users through account setup and app features. - -### Core Elements - -- **Welcome Screen**: - - Brief intro to the app. - - Buttons to log in or sign up. -- **Sign-Up Page**: - - Fields for username, email, password. - - Option to sign up via Google or Facebook. -- **Login Page**: - - Username and password fields. - - Forgot Password link. - -### Content Display - -- Minimal text to streamline onboarding. -- Prominent call-to-action buttons. - -### Navigation and Routes - -- `/welcome`, `/signup`, `/login`. - -### Restrictions - -- Accessible to all users without restrictions. - ---- - -## **8. Error and Offline Pages** - -### Page Purpose - -Provide feedback for errors and offline scenarios. - -### Core Elements - -- **404 Page**: - - Message indicating content not found. - - Button to return to Home. -- **Offline Mode**: - - Notification about internet connectivity issues. - - Limited access to downloaded or cached content. - -### Content Display - -- Clear messaging and actionable steps for users. - -### Navigation and Routes - -- `/404`, `/offline`. - -### Restrictions - -- Accessible in all scenarios. - ---- - -## **Dynamic Content and Restrictions Summary** - -- **Dynamic Content**: - - Recommendations, playlists, recently played, and search results update in real-time based on user activity. -- **Restrictions**: - - Login required for personalized content, library access, and playback history. diff --git a/backend/src/build-system/__tests__/db-requirement-document.md b/backend/src/build-system/__tests__/db-requirement-document.md deleted file mode 100644 index 3b0d0881..00000000 --- a/backend/src/build-system/__tests__/db-requirement-document.md +++ /dev/null @@ -1,230 +0,0 @@ -### Database Requirements Document - -#### 1. Overview - -- **Project Scope**: Design and implement a database to support a Spotify-like music web application, facilitating personalized music streaming, content management, and user interaction. -- **Database Purpose**: Store and manage user profiles, music content, playlists, playback states, and preferences to support dynamic, personalized user experiences. -- **General Requirements**: - - Ensure high availability and scalability to handle concurrent user activity. - - Support real-time data updates for personalized recommendations and playback. - - Ensure data integrity and enforce business rules. - ---- - -#### 2. Entity Definitions - -##### User - -- **Description**: Represents registered users of the application. -- **Business Rules**: - - Each user must have a unique email. - - Users can manage their preferences and account details. -- **Key Attributes**: - - `user_id` (Primary Key) - - `username` (Unique, required) - - `email` (Unique, required) - - `password_hash` (Required) - - `subscription_type` (e.g., Free, Premium) - - `preferences` (e.g., theme, audio quality) - - `created_at`, `updated_at` -- **Relationships**: - - One-to-many with `Playlist`. - - Many-to-many with `Song` for liked songs. - -##### Song - -- **Description**: Represents individual songs available on the platform. -- **Business Rules**: - - Each song must have an associated album and artist. - - Songs may belong to multiple playlists. -- **Key Attributes**: - - `song_id` (Primary Key) - - `title` (Required) - - `artist_id` (Foreign Key) - - `album_id` (Foreign Key) - - `duration` (In seconds) - - `genre` (Category) - - `release_date` -- **Relationships**: - - Many-to-one with `Album` and `Artist`. - - Many-to-many with `Playlist`. - -##### Artist - -- **Description**: Represents artists whose songs are on the platform. -- **Key Attributes**: - - `artist_id` (Primary Key) - - `name` (Required) - - `bio` - - `profile_image` - - `created_at`, `updated_at` -- **Relationships**: - - One-to-many with `Song` and `Album`. - -##### Album - -- **Description**: Represents music albums. -- **Key Attributes**: - - `album_id` (Primary Key) - - `title` (Required) - - `artist_id` (Foreign Key) - - `release_date` - - `cover_image` -- **Relationships**: - - One-to-many with `Song`. - -##### Playlist - -- **Description**: Represents user-created or curated playlists. -- **Business Rules**: - - A playlist must belong to a user or be globally curated. -- **Key Attributes**: - - `playlist_id` (Primary Key) - - `name` (Required) - - `user_id` (Foreign Key, nullable for curated playlists) - - `description` - - `is_curated` (Boolean) - - `created_at`, `updated_at` -- **Relationships**: - - Many-to-many with `Song`. - -##### PlaybackState - -- **Description**: Tracks the playback state for a user. -- **Key Attributes**: - - `playback_id` (Primary Key) - - `user_id` (Foreign Key) - - `current_song_id` (Foreign Key) - - `queue` (Array of `song_id`s) - - `playback_position` (Seconds) - - `volume` - - `created_at`, `updated_at` - -##### Recommendation - -- **Description**: Stores dynamic recommendations for users. -- **Key Attributes**: - - `recommendation_id` (Primary Key) - - `user_id` (Foreign Key) - - `content` (JSON: list of recommended songs, albums, playlists) - - `generated_at` - ---- - -#### 3. Data Requirements - -##### User - -- Fields: - - `user_id`: UUID - - `username`: String (max 50) - - `email`: String (unique, max 100) - - `password_hash`: String - - `subscription_type`: Enum (Free, Premium) - - `preferences`: JSON - - `created_at`, `updated_at`: Timestamps -- Constraints: - - `email` and `username` must be unique. - - Enforce non-null constraints on required fields. -- Indexing: - - Index on `email` and `user_id`. - -##### Song - -- Fields: - - `song_id`: UUID - - `title`: String (max 100) - - `artist_id`, `album_id`: Foreign Keys - - `duration`: Integer - - `genre`: String - - `release_date`: Date -- Constraints: - - Non-null constraints on `title`, `artist_id`, and `album_id`. -- Indexing: - - Index on `title` and `genre`. - -##### Playlist - -- Fields: - - `playlist_id`: UUID - - `name`: String (max 50) - - `user_id`: Foreign Key - - `description`: String - - `is_curated`: Boolean - - `created_at`, `updated_at`: Timestamps -- Constraints: - - Enforce foreign key constraints for `user_id`. -- Indexing: - - Index on `user_id`. - -##### PlaybackState - -- Fields: - - `playback_id`: UUID - - `user_id`: Foreign Key - - `current_song_id`: Foreign Key - - `queue`: JSON - - `playback_position`: Integer - - `volume`: Float - - `created_at`, `updated_at`: Timestamps -- Constraints: - - Ensure valid `user_id` and `current_song_id`. - ---- - -#### 4. Relationships - -- `User` to `Playlist`: One-to-many. -- `Playlist` to `Song`: Many-to-many (junction table: `playlist_song`). -- `Song` to `Album`: Many-to-one. -- `Song` to `Artist`: Many-to-one. -- `User` to `PlaybackState`: One-to-one. -- Referential Integrity: - - Cascade delete for dependent entities (e.g., playlists when a user is deleted). - ---- - -#### 5. Data Access Patterns - -- Common Queries: - - Fetch user playlists, liked songs, and playback state. - - Search for songs, albums, or artists. - - Fetch recommended content dynamically. -- Indexing: - - Full-text search for song titles and artist names. - - Index on foreign keys for join performance. - ---- - -#### 6. Security Requirements - -- Access Control: - - Restrict user data to authenticated sessions. -- Data Privacy: - - Hash sensitive data (e.g., passwords). -- Audit: - - Log user activity and data changes. - ---- - -#### 7. Performance Requirements - -- Expected Volume: - - Millions of songs and playlists. - - Thousands of concurrent users. -- Growth: - - Plan for 10x growth in user and song data over 5 years. -- Optimizations: - - Cache frequently accessed data (e.g., recommendations). - - Use partitioning for large tables. - ---- - -#### 8. Additional Considerations - -- Backups: - - Automated daily backups. -- Archiving: - - Move inactive playlists to archival storage after 1 year. -- Integration: - - Support for third-party authentication and external APIs. diff --git a/backend/src/build-system/__tests__/file-arch.md b/backend/src/build-system/__tests__/file-arch.md deleted file mode 100644 index cc33d3e4..00000000 --- a/backend/src/build-system/__tests__/file-arch.md +++ /dev/null @@ -1,129 +0,0 @@ - -{ - "files": { - "src/api/auth.ts": { - "dependsOn": [] - }, - "src/api/music.ts": { - "dependsOn": [] - }, - "src/api/user.ts": { - "dependsOn": [] - }, - "src/api/index.ts": { - "dependsOn": ["./auth", "./music", "./user"] - }, - "src/components/common/Button/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/common/Input/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/common/Loader/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/layout/Header/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/layout/Footer/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/layout/Sidebar/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/specific/MusicDetail/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/specific/Playlist/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/specific/Search/index.tsx": { - "dependsOn": ["./index.css"] - }, - "src/components/index.ts": { - "dependsOn": ["./common/Button", "./common/Input", "./common/Loader", "./layout/Header", "./layout/Footer", "./layout/Sidebar", "./specific/MusicDetail", "./specific/Playlist", "./specific/Search"] - }, - "src/contexts/AuthContext.tsx": { - "dependsOn": [] - }, - "src/contexts/ThemeContext.tsx": { - "dependsOn": [] - }, - "src/contexts/PlayerContext.tsx": { - "dependsOn": [] - }, - "src/contexts/index.ts": { - "dependsOn": ["./AuthContext", "./ThemeContext", "./PlayerContext"] - }, - "src/hooks/useAuth.ts": { - "dependsOn": ["../contexts/AuthContext"] - }, - "src/hooks/useMusic.ts": { - "dependsOn": [] - }, - "src/hooks/useUser.ts": { - "dependsOn": ["../contexts/AuthContext"] - }, - "src/hooks/index.ts": { - "dependsOn": ["./useAuth", "./useMusic", "./useUser"] - }, - "src/pages/Home/index.tsx": { - "dependsOn": ["./Home.css", "../../components", "../../hooks/useMusic", "../../hooks/useUser"] - }, - "src/pages/Home/Home.css": { - "dependsOn": [] - }, - "src/pages/Discover/index.tsx": { - "dependsOn": ["./Discover.css", "../../components", "../../hooks/useMusic"] - }, - "src/pages/Discover/Discover.css": { - "dependsOn": [] - }, - "src/pages/Playlists/index.tsx": { - "dependsOn": ["./Playlists.css", "../../components", "../../hooks/useMusic", "../../hooks/useUser"] - }, - "src/pages/Playlists/Playlists.css": { - "dependsOn": [] - }, - "src/pages/Artists/index.tsx": { - "dependsOn": ["./Artists.css", "../../components"] - }, - "src/pages/Artists/Artists.css": { - "dependsOn": [] - }, - "src/pages/Profile/index.tsx": { - "dependsOn": ["./Profile.css", "../../components", "../../hooks/useUser"] - }, - "src/pages/Profile/Profile.css": { - "dependsOn": [] - }, - "src/pages/Login/index.tsx": { - "dependsOn": ["./Login.css", "../../components", "../../hooks/useAuth"] - }, - "src/pages/Login/Login.css": { - "dependsOn": [] - }, - "src/pages/index.ts": { - "dependsOn": ["./Home", "./Discover", "./Playlists", "./Artists", "./Profile", "./Login"] - }, - "src/router.ts": { - "dependsOn": ["./pages"] - }, - "src/index.ts": { - "dependsOn": ["./router"] - }, - "src/utils/constants.ts": { - "dependsOn": [] - }, - "src/utils/helpers.ts": { - "dependsOn": [] - }, - "src/utils/validators.ts": { - "dependsOn": [] - }, - "src/utils/index.ts": { - "dependsOn": ["./constants", "./helpers", "./validators"] - } - } -} - diff --git a/backend/src/build-system/__tests__/file-structure-document.md b/backend/src/build-system/__tests__/file-structure-document.md deleted file mode 100644 index b12484e1..00000000 --- a/backend/src/build-system/__tests__/file-structure-document.md +++ /dev/null @@ -1,77 +0,0 @@ -src/ -├── api/ # API logic for interacting with backend services -│ ├── auth.ts # Authentication API logic -│ ├── music.ts # API for music-related actions (e.g., fetching playlists, songs) -│ ├── user.ts # API for user-related actions (e.g., profile updates) -│ └── index.ts # Exports all API modules for centralized access -├── components/ # Reusable UI components -│ ├── common/ # Generic components used across the app -│ │ ├── Button/ # Button component folder -│ │ │ ├── index.tsx # Main Button component file -│ │ │ └── index.css # Button-specific styles -│ │ ├── Input/ # Input field component folder -│ │ │ ├── index.tsx # Main Input component file -│ │ │ └── index.css # Input-specific styles -│ │ ├── Modal/ # Modal component folder -│ │ │ ├── index.tsx # Main Modal component file -│ │ │ └── index.css # Modal-specific styles -│ ├── layout/ # Components for layout and structure -│ │ ├── Header/ # Header component folder -│ │ │ ├── index.tsx # Main Header component file -│ │ │ └── index.css # Header-specific styles -│ │ ├── Footer/ # Footer component folder -│ │ │ ├── index.tsx # Main Footer component file -│ │ │ └── index.css # Footer-specific styles -│ ├── specific/ # Page-specific components -│ │ ├── Home/ # Components specific to the Home page -│ │ │ ├── FeaturedPlaylists.tsx # Component for featured playlists section -│ │ │ ├── Recommendations.tsx # Component for personalized recommendations -│ │ │ └── NewReleases.tsx # Component for new releases -│ │ ├── Search/ # Components specific to the Search page -│ │ │ ├── SearchResults.tsx # Component for displaying search results -│ │ │ └── Autocomplete.tsx # Component for search suggestions -│ │ ├── Library/ # Components specific to the Library page -│ │ │ ├── MyPlaylists.tsx # Component for user playlists -│ │ │ ├── LikedSongs.tsx # Component for liked songs -│ │ │ └── RecentlyPlayed.tsx # Component for recently played songs -├── contexts/ # Context providers for global state -│ ├── AuthContext.tsx # Provides user authentication state -│ ├── ThemeContext.tsx # Provides theme (dark/light mode) state -│ ├── PlayerContext.tsx # Provides music player state and controls -│ └── index.ts # Centralized export for contexts -├── hooks/ # Custom hooks for data fetching and state management -│ ├── useAuth.ts # Hook for user authentication logic -│ ├── usePlayer.ts # Hook for controlling the music player -│ ├── useTheme.ts # Hook for managing theme preferences -│ └── useFetch.ts # Generic hook for data fetching -├── pages/ # Route-specific views -│ ├── Home/ # Home page folder -│ │ ├── index.tsx # Main Home page component -│ │ └── index.css # Home page-specific styles -│ ├── Search/ # Search page folder -│ │ ├── index.tsx # Main Search page component -│ │ └── index.css # Search page-specific styles -│ ├── Library/ # Library page folder -│ │ ├── index.tsx # Main Library page component -│ │ └── index.css # Library page-specific styles -│ ├── Playlist/ # Playlist page folder -│ │ ├── index.tsx # Main Playlist page component -│ │ └── index.css # Playlist page-specific styles -│ ├── Account/ # Account page folder -│ │ ├── index.tsx # Main Account page component -│ │ └── index.css # Account page-specific styles -│ ├── Player/ # Player page folder -│ │ ├── index.tsx # Main Player page component -│ │ └── index.css # Player page-specific styles -│ ├── Error/ # Error and Offline pages folder -│ │ ├── NotFound.tsx # 404 Page component -│ │ ├── Offline.tsx # Offline mode page component -│ │ └── index.css # Styles for error pages -├── utils/ # Utility functions -│ ├── constants.ts # Application-wide constants -│ ├── helpers.ts # Helper functions -│ ├── validators.ts # Validation logic -│ └── index.ts # Centralized export for utilities -├── router.ts # Central routing configuration -├── index.tsx # Application entry point -└── App.tsx # Main application component From 2fe9f033188cbf2ce969077615f4686a0013b588 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 16:35:09 -0500 Subject: [PATCH 32/40] remove log --- backend/src/build-system/context.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/src/build-system/context.ts b/backend/src/build-system/context.ts index d1354cda..1b3b3455 100644 --- a/backend/src/build-system/context.ts +++ b/backend/src/build-system/context.ts @@ -7,7 +7,7 @@ import { BuildSequence, } from './types'; import { Logger } from '@nestjs/common'; -import { VirtualDirectory } from './node/file-generate/virtual-directory'; +import { VirtualDirectory } from './virtual-dir'; export type GlobalDataKeys = 'projectName' | 'description' | 'platform'; type ContextData = { @@ -74,8 +74,7 @@ export class BuilderContext { if (!node) { throw new Error(`Node not found: ${nodeId}`); } - console.log('canExecute id: ' + node.name); - console.log('canExecute: ' + this.canExecute(nodeId)); + if (!this.canExecute(nodeId)) { throw new Error(`Dependencies not met for node: ${nodeId}`); } From 43d513a7ef34258740f467387de7ccc5206d1d42 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 16:43:21 -0500 Subject: [PATCH 33/40] remove test --- backend/src/build-system/context.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/src/build-system/context.ts b/backend/src/build-system/context.ts index 1b3b3455..b20d9728 100644 --- a/backend/src/build-system/context.ts +++ b/backend/src/build-system/context.ts @@ -128,11 +128,6 @@ export class BuilderContext { node: BuildNode, args: unknown, ): Promise { - // if (process.env.NODE_ENV === 'test') { - // this.logger.log(`[TEST] Executing node: ${node.id}`); - // return { success: true, data: { nodeId: node.id } }; - // } - this.logger.log(`Executing node: ${node.id}`); const handler = this.handlerManager.getHandler(node.id); if (!handler) { From 16c09a5e474f2e9580466b3c8772b1bc314bdba8 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 17:28:26 -0500 Subject: [PATCH 34/40] add comment --- backend/src/build-system/node/file-generate/index.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index bfd3495a..988798b5 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -2,7 +2,7 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { Logger } from '@nestjs/common'; import * as toposort from 'toposort'; -import { VirtualDirectory } from './virtual-directory'; +import { VirtualDirectory } from '../../virtual-dir'; import { BuilderContext } from 'src/build-system/context'; import { BuildHandler, BuildResult } from 'src/build-system/types'; @@ -10,10 +10,6 @@ export class FileGeneratorHandler { private readonly logger = new Logger('FileGeneratorHandler'); private virtualDir: VirtualDirectory; - constructor() { - this.virtualDir = new VirtualDirectory(); - } - async run(context: BuilderContext, args: unknown): Promise { const fileArch = args[0] as string; @@ -54,7 +50,7 @@ export class FileGeneratorHandler { for (const file of sortedFiles) { const fullPath = path.resolve(projectSrcPath, file); this.logger.log(`Generating file in dependency order: ${fullPath}`); - // to do + // TODO(allen) await this.createFile(fullPath); } From 532e57b9ec9776f2fe9282a43aef76f1341d45cd Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 17:28:51 -0500 Subject: [PATCH 35/40] move under build system --- .../{node/file-generate/Virtual-Directory.ts => virtual-dir.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename backend/src/build-system/{node/file-generate/Virtual-Directory.ts => virtual-dir.ts} (100%) diff --git a/backend/src/build-system/node/file-generate/Virtual-Directory.ts b/backend/src/build-system/virtual-dir.ts similarity index 100% rename from backend/src/build-system/node/file-generate/Virtual-Directory.ts rename to backend/src/build-system/virtual-dir.ts From 7083c52b7fe494dfa1077303cef52219dc1a8290 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 17:42:23 -0500 Subject: [PATCH 36/40] use normalize-path --- backend/src/build-system/virtual-dir.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/src/build-system/virtual-dir.ts b/backend/src/build-system/virtual-dir.ts index c5b96326..d9d568c4 100644 --- a/backend/src/build-system/virtual-dir.ts +++ b/backend/src/build-system/virtual-dir.ts @@ -1,4 +1,5 @@ import * as path from 'path'; +import * as normalizePath from 'normalize-path'; interface VirtualNode { name: string; @@ -69,7 +70,7 @@ export class VirtualDirectory { } private findNode(inputPath: string): VirtualNode | null { - const normalizedPath = this.normalizePath(inputPath); + const normalizedPath = this.normalize_Path(inputPath); const parts = normalizedPath.split('/').filter(Boolean); if (parts[0] !== 'src') { @@ -85,14 +86,14 @@ export class VirtualDirectory { return current; } - private normalizePath(inputPath: string): string { - return path.normalize(inputPath).replace(/\\/g, '/').replace(/\/$/, ''); + private normalize_Path(inputPath: string): string { + return normalizePath(inputPath); } resolveRelativePath(fromFile: string, toPath: string): string { const fromDir = path.dirname(fromFile); const resolvedPath = path.join(fromDir, toPath).replace(/\\/g, '/'); - return this.normalizePath(resolvedPath); + return this.normalize_Path(resolvedPath); } getAllFiles(): string[] { From eb7f38beae0927d05abf0a471d65a6f53752bad4 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 17:47:39 -0500 Subject: [PATCH 37/40] use normalize-path --- .../__tests__/testVirtualDir.spec.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/backend/src/build-system/__tests__/testVirtualDir.spec.ts b/backend/src/build-system/__tests__/testVirtualDir.spec.ts index d7261bb7..0de61dba 100644 --- a/backend/src/build-system/__tests__/testVirtualDir.spec.ts +++ b/backend/src/build-system/__tests__/testVirtualDir.spec.ts @@ -1,21 +1,13 @@ import * as fs from 'fs-extra'; import * as path from 'path'; -import { FileGeneratorHandler } from '../node/file-generate'; -import { VirtualDirectory } from '../node/file-generate/virtual-directory'; +import { VirtualDirectory } from '../virtual-dir'; +import * as normalizePath from 'normalize-path'; -describe('FileGeneratorHandler and VirtualDirectory', () => { - const structMdFilePath = path.resolve( +describe('VirtualDirectory', () => { + const structMdFilePath = normalizePath( 'src\\build-system\\__tests__\\file-structure-document.md', ); - beforeEach(async () => { - await fs.remove('src\\build-system\\__tests__\\test-project\\src\\'); - }); - - afterEach(async () => { - await fs.remove('src\\build-system\\__tests__\\test-project\\src\\'); - }); - describe('VirtualDirectory', () => { let virtualDir: VirtualDirectory; let structMarkdownContent: string; From d39b855345e093d00fdb07788cc169918a7ebc23 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 21:08:57 -0500 Subject: [PATCH 38/40] add retry for arch and move extractJsonFromMarkdown to util --- .../src/build-system/node/file-arch/index.ts | 84 +++++++++++++++++-- .../build-system/node/file-generate/index.ts | 61 +------------- backend/src/build-system/util.ts | 30 +++++++ 3 files changed, 107 insertions(+), 68 deletions(-) create mode 100644 backend/src/build-system/util.ts diff --git a/backend/src/build-system/node/file-arch/index.ts b/backend/src/build-system/node/file-arch/index.ts index 25cd1c9c..280251b2 100644 --- a/backend/src/build-system/node/file-arch/index.ts +++ b/backend/src/build-system/node/file-arch/index.ts @@ -2,6 +2,7 @@ import { BuildHandler, BuildResult } from 'src/build-system/types'; import { BuilderContext } from 'src/build-system/context'; import { generateFileArchPrompt } from './prompt'; import { Logger } from '@nestjs/common'; +import { FileUtil } from 'src/build-system/util'; export class FileArchGenerateHandler implements BuildHandler { readonly id = 'op:FILE_ARCH::STATE:GENERATE'; @@ -27,20 +28,85 @@ export class FileArchGenerateHandler implements BuildHandler { JSON.stringify(fileStructure, null, 2), JSON.stringify(dataMapStruct, null, 2), ); - this.logger.log( - 'generateFileArchPrompt: ' + JSON.stringify(prompt, null, 2), - ); - const fileArchContent = await context.model.chatSync( - { - content: prompt, - }, - 'gpt-4o-mini', - ); + // fileArchContent generate + let successBuild = false; + let fileArchContent = null; + let jsonData = null; + let retry = 0; + const retryChances = 2; + while (!successBuild) { + if (retry > retryChances) { + throw new Error( + 'Failed to build virtual directory after multiple attempts', + ); + } + + fileArchContent = await context.model.chatSync( + { + content: prompt, + }, + 'gpt-4o-mini', + ); + + // validation test + jsonData = FileUtil.extractJsonFromMarkdown(fileArchContent); + if (jsonData == null) { + retry += 1; + this.logger.error('Extract Json From Markdown fail'); + continue; + } + + if (!this.validateJsonData(jsonData)) { + retry += 1; + this.logger.error('FileArchGenerate validateJsonData fail'); + continue; + } + this.logger.log(jsonData); + successBuild = true; + } return { success: true, data: fileArchContent, }; } + + /** + * Validate the structure and content of the JSON data. + * @param jsonData The JSON data to validate. + * @throws Error if validation fails. + */ + private validateJsonData(jsonData: { + files: Record; + }): boolean { + const validPathRegex = /^[a-zA-Z0-9_\-/.]+$/; + + for (const [file, details] of Object.entries(jsonData.files)) { + // Validate the file path + if (!validPathRegex.test(file)) { + this.logger.error(`Invalid file path: ${file}`); + return false; + } + + // Validate dependencies + for (const dependency of details.dependsOn) { + if (!validPathRegex.test(dependency)) { + this.logger.error( + `Invalid dependency path "${dependency}" in file "${file}".`, + ); + return false; + } + + // Ensure no double slashes or trailing slashes + if (dependency.includes('//') || dependency.endsWith('/')) { + this.logger.error( + `Malformed dependency path "${dependency}" in file "${file}".`, + ); + return false; + } + } + } + return true; + } } diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index 988798b5..12c7e614 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -5,6 +5,7 @@ import * as toposort from 'toposort'; import { VirtualDirectory } from '../../virtual-dir'; import { BuilderContext } from 'src/build-system/context'; import { BuildHandler, BuildResult } from 'src/build-system/types'; +import { FileUtil } from 'src/build-system/util'; export class FileGeneratorHandler { private readonly logger = new Logger('FileGeneratorHandler'); @@ -32,10 +33,7 @@ export class FileGeneratorHandler { markdownContent: string, projectSrcPath: string, ): Promise<{ success: boolean; data: string }> { - // add test for validate - const jsonData = this.extractJsonFromMarkdown(markdownContent); - this.validateJsonData(jsonData); - + const jsonData = FileUtil.extractJsonFromMarkdown(markdownContent); // Build the dependency graph and detect cycles before any file operations const { graph, nodes } = this.buildDependencyGraph(jsonData); this.detectCycles(graph); @@ -122,40 +120,6 @@ export class FileGeneratorHandler { return sortedFiles; } - /** - * Validate the structure and content of the JSON data. - * @param jsonData The JSON data to validate. - * @throws Error if validation fails. - */ - private validateJsonData(jsonData: { - files: Record; - }): void { - const validPathRegex = /^[a-zA-Z0-9_\-/.]+$/; - - for (const [file, details] of Object.entries(jsonData.files)) { - // Validate the file path - if (!validPathRegex.test(file)) { - throw new Error(`Invalid file path: ${file}`); - } - - // Validate dependencies - for (const dependency of details.dependsOn) { - if (!validPathRegex.test(dependency)) { - throw new Error( - `Invalid dependency path "${dependency}" in file "${file}".`, - ); - } - - // Ensure no double slashes or trailing slashes - if (dependency.includes('//') || dependency.endsWith('/')) { - throw new Error( - `Malformed dependency path "${dependency}" in file "${file}".`, - ); - } - } - } - } - /** * Resolve a dependency path relative to the current file. * @param currentFile The current file's path. @@ -178,27 +142,6 @@ export class FileGeneratorHandler { return resolvedPath; } - /** - * Extract JSON data from Markdown content. - * @param markdownContent The Markdown content containing the JSON. - */ - private extractJsonFromMarkdown(markdownContent: string): { - files: Record; - } { - const jsonMatch = /([\s\S]*?)<\/GENERATEDCODE>/m.exec( - markdownContent, - ); - if (!jsonMatch) { - throw new Error('No JSON found in the provided Markdown content.'); - } - - try { - return JSON.parse(jsonMatch[1]); - } catch (error) { - throw new Error('Invalid JSON format in the Markdown content.'); - } - } - /** * Validate that all files and dependencies exist in the virtual directory structure * @param nodes Set of all files and dependencies diff --git a/backend/src/build-system/util.ts b/backend/src/build-system/util.ts new file mode 100644 index 00000000..7283fedc --- /dev/null +++ b/backend/src/build-system/util.ts @@ -0,0 +1,30 @@ +import { Logger } from '@nestjs/common'; + +export class FileUtil { + private static readonly logger = new Logger('FileUtil'); + + /** + * Extract JSON data from Markdown content. + * @param markdownContent The Markdown content containing the JSON. + */ + static extractJsonFromMarkdown(markdownContent: string): { + files: Record; + } { + const jsonMatch = /([\s\S]*?)<\/GENERATEDCODE>/m.exec( + markdownContent, + ); + if (!jsonMatch) { + FileUtil.logger.error('No JSON found in the provided Markdown content.'); + return null; + } + + try { + return JSON.parse(jsonMatch[1]); + } catch (error) { + FileUtil.logger.error( + 'Invalid JSON format in the Markdown content: ' + error, + ); + return null; + } + } +} From 445aaabfcb538a00a133228159374640a83c94f6 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 21:31:25 -0500 Subject: [PATCH 39/40] update --- backend/src/build-system/context.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/backend/src/build-system/context.ts b/backend/src/build-system/context.ts index b20d9728..936fe653 100644 --- a/backend/src/build-system/context.ts +++ b/backend/src/build-system/context.ts @@ -49,16 +49,7 @@ export class BuilderContext { return false; } - const unmetDependencies = - node.requires?.filter((dep) => !this.state.completed.has(dep)) || []; - if (unmetDependencies.length > 0) { - console.log( - `Node ${nodeId} has unmet dependencies: ${unmetDependencies}`, - ); - return false; - } - - return true; + return !node.requires?.some((dep) => !this.state.completed.has(dep)); } private findNode(nodeId: string): BuildNode | null { From 78d758c6737639fa7591a3477330fc357e62690d Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 10 Dec 2024 22:02:58 -0500 Subject: [PATCH 40/40] init context --- backend/src/build-system/context.ts | 2 +- backend/src/build-system/node/file-generate/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/build-system/context.ts b/backend/src/build-system/context.ts index 936fe653..cb98691d 100644 --- a/backend/src/build-system/context.ts +++ b/backend/src/build-system/context.ts @@ -27,7 +27,7 @@ export class BuilderContext { private results: Map = new Map(); private handlerManager: BuildHandlerManager; public model: ModelProvider; - private virtualDirectory: VirtualDirectory; + public virtualDirectory: VirtualDirectory; constructor( private sequence: BuildSequence, diff --git a/backend/src/build-system/node/file-generate/index.ts b/backend/src/build-system/node/file-generate/index.ts index 12c7e614..446084f7 100644 --- a/backend/src/build-system/node/file-generate/index.ts +++ b/backend/src/build-system/node/file-generate/index.ts @@ -12,6 +12,7 @@ export class FileGeneratorHandler { private virtualDir: VirtualDirectory; async run(context: BuilderContext, args: unknown): Promise { + this.virtualDir = context.virtualDirectory; const fileArch = args[0] as string; // change here